/*
 * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
 * Copyright (C) 2007 Holger Hans Peter Freyther
 * Copyright (C) 2008 Collabora Ltd.
 * Copyright (C) 2008 Nuanti Ltd.
 * Copyright (C) 2009 Appcelerator Inc.
 * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org>
 * All rights reserved.
 * Copyright (C) 2012, 2013 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "ResourceHandleManager.h"

#if !USE(NTF)

#include "CredentialBackingStoreManx.h"
#include "CredentialTransformDataManx.h"
#include "DataURL.h"
#include "DocumentLoader.h"
#include "FrameLoaderClient.h"
#include "HTTPParsers.h"
#include "MIMETypeRegistry.h"
#include "NetworkStateNotifier.h"
#include "NotImplemented.h"
#include "ResourceError.h"
#include "ResourceHandle.h"
#include "ResourceHandleInternal.h"
#include "RunLoop.h"
#include <errno.h>
#include <manx/CookieJar.h>
#include <manx/DNSPrefetch.h>
#include <manx/Date.h>
#include <manx/Locale.h>
#include <manx/NetworkMemory.h>
#include <manx/NetworkProfile.h>
#include <manx/ResourceRequestLogger.h>
#include <manx/WebSecurity.h>
#include <stdio.h>
#include <unistd.h>
#include <wtf/HashSet.h>
#include <wtf/ThreadingPrimitives.h>

#if OS(PSP2)
#define USE_SCRACHPAD_COOKIEJAR 1
#endif

#if USE_SCRACHPAD_COOKIEJAR
extern "C" {
bool scrachpadmatch(const char *path);
char* makescrachpadpath(const char *path);
void freescrachpadpath(char *path);
void clearscrachpad();
}
#endif

#if OS(PSP2)
#define SELECT_TIMEOUT_VALUE 100
#else
#define SELECT_TIMEOUT_VALUE -1
#endif

// Reuse curl easy handle to keep connections in synchronous operations. In asynchronous operations,
// connection pool is used by 'multi' handle. Note that to keep connection use curl_easy_reset()
// instead of curl_easy_cleanup().
#if OS(ORBIS)
#define ENABLE_PERSISTENT_CONNECTION_FOR_SYNCHRONOUS_COMMUNICATION
#endif
#ifdef ENABLE_PERSISTENT_CONNECTION_FOR_SYNCHRONOUS_COMMUNICATION

static pthread_key_t curlEasyHandleForSyncKey;
static pthread_once_t curlEasyHandleForSyncKeyOnce = PTHREAD_ONCE_INIT;

static void finalizeCurlEasyHandleForSync(void* tsd)
{
    curl_easy_cleanup(reinterpret_cast<CURL*>(tsd));
}

static void initializeCurlEasyHandleForSyncKey()
{
    pthread_key_create(&curlEasyHandleForSyncKey, finalizeCurlEasyHandleForSync);
}

static CURL* curlEasyHandleForSync()
{
    pthread_once(&curlEasyHandleForSyncKeyOnce, initializeCurlEasyHandleForSyncKey);
    CURL* handle = reinterpret_cast<CURL*>(pthread_getspecific(curlEasyHandleForSyncKey));
    if (!handle) {
        handle = curl_easy_init();
        pthread_setspecific(curlEasyHandleForSyncKey, handle);
    }
    return handle;
}
#endif

#define RESOURCEHANDLE_COMMAND_DATA_MAX    7

class ResourceHandleCommand {
public:
    union data {
        int typeInt;
        long long int typeLongLongInt;
        bool typeBool;
        char typeChar;
        char* typeCharPtr;
        void* typeVoidPtr;
        const char* typeConstCharPtr;
        const void* typeConstVoidPtr;
    };

    ResourceHandleCommand()
    {
        memset(m_data, 0x00, sizeof(union data) * RESOURCEHANDLE_COMMAND_DATA_MAX);
    }

    ~ResourceHandleCommand()
    {
    }

    union data m_data[RESOURCEHANDLE_COMMAND_DATA_MAX];
};

#define ASYNC_REQUEST_DEBUG 0
// #define DISABLE_X_FRAME_OPTIONS
// #define DEBUG_CURL 0

#if ASYNC_REQUEST_DEBUG
#define DEBUG_LOG(...) printf(__VA_ARGS__)
#define STDERR_LOG(...) fprintf(stderr, __VA_ARGS__)
static int jobscount = 0;
#else
#define DEBUG_LOG(...)
#define STDERR_LOG(...)
#endif

namespace WebCore {

// functions running on webkit thread and receiving and translating network message
// from worker thread.
static void didRecvData(void* cmd);
static void didRecvResponse(void* cmd);
#if OS(ORBIS)
static void willFilterSendRequest(void* cmd);
#endif
static void willSendRequest(void* cmd);
static void didFinish(void* cmd);
static void didFail(void* cmd);
static void handleCancel(void* cmd);
static void handleResponseHeaders(void* cmd);
static void notifyOriginFrame(void* cmd);
static CURLcode sslctxfun(CURL* curlhandle, void* sslctx, void* param);
static void replaceUrlOfMainResource(void* data);

// functions running on worker thread and posting network message to webkit thread.
void postDidRecvData(ResourceHandleContext*, void* ptr, const char* url, size_t totalSize);
void postDidRecvResponse(ResourceHandleContext*);
#if OS(ORBIS)
void postWillFilterSendRequest(ResourceHandleContext*);
#endif
void postWillSendRequest(ResourceHandleContext*);
void postHandleCancel(ResourceHandleContext*);
void postDidFinish(ResourceHandleContext*);
void postDidFail(ResourceHandleContext*);
void postHandleResponseHeaders(ResourceHandleContext*);
void postNotifyOriginFrame(void* context);
void saveResponseHeader(ResourceHandleContext*, char* ptr, size_t totalSize);

// functions for url filtering
static bool evaluateFilteringResult(ResourceHandleContext* , bool&);
static bool convertToFilteringUrl(ResourceHandleContext* , bool&);

// resources which are used to synchronize thread messages
// between webkit-thread and worker-thread.
WTF::ThreadIdentifier ResourceHandleManager::s_verifySslThreadId = 0;
WTF::ThreadIdentifier ResourceHandleManager::s_workerThreadId = 0;
WTF::ThreadIdentifier ResourceHandleManager::s_filteringThreadId = 0;
WTF::Mutex* ResourceHandleManager::s_verifySslThreadMutex = 0;
WTF::Mutex* ResourceHandleManager::s_verifySslThreadConditionMutex = 0;
WTF::Mutex* ResourceHandleManager::s_workerThreadMutex = 0;
WTF::Mutex* ResourceHandleManager::s_workerThreadConditionMutex = 0;
WTF::Mutex* ResourceHandleManager::s_filteringThreadMutex = 0;
WTF::Mutex* ResourceHandleManager::s_filteringThreadConditionMutex = 0;
WTF::Mutex* ResourceHandleManager::s_notifyBadCertConditionMutex = 0;
WTF::Mutex* ResourceHandleManager::s_setupMutex = 0;
WTF::ThreadCondition* ResourceHandleManager::s_verifySslThreadCondition = 0;
WTF::ThreadCondition* ResourceHandleManager::s_workerThreadCondition = 0;
WTF::ThreadCondition* ResourceHandleManager::s_filteringThreadCondition = 0;
WTF::ThreadCondition* ResourceHandleManager::s_notifyBadCertCondition = 0;
static bool s_filteringThreadCreated = false;
static WTF::ThreadCondition* s_filteringThreadCreatedCondition = 0;
static WTF::Mutex* s_filteringThreadCreatedConditionMutex = 0;

// curl multi handles which retains live network context including 
// various information about url, header, request body and etc.
static CURLM* s_workerMultiHandle = 0;
static WTF::Mutex* s_workerThreadMultiMutex = 0;
static CURLM* s_filteringMultiHandle = 0;

// default timeouts
static long s_connectTimeout = 60;
static long s_dnsCacheTimeout = 60 * 5;

#ifndef NDEBUG
static ThreadIdentifier workerThreadIdentifier;
#if OS(ORBIS)
static bool isWorkerThread()
{
    return currentThread() == workerThreadIdentifier;
}
#endif
#endif

#if OS(ORBIS) || OS(PSP2)
static void resetSslConn(ResourceHandleContext* context)
{
    ResourceHandleManager::sharedInstance()->initializeHandleForAsync(context);
    curl_easy_setopt(context->m_handle, CURLOPT_SSL_VERIFYHOST, false);
    curl_easy_setopt(context->m_handle, CURLOPT_SSL_VERIFYPEER, false);
}

// map between ResourceHandle and ResourceHandleContext
static HashMap<ResourceHandle*, ResourceHandleContext*> s_jobToContextMap;
static Vector<ResourceHandleContext*> s_cancelContextList;
#endif
static bool s_initialized = false;
static bool s_exit = false;

static WTF::HashSet<unsigned> s_userAcceptedHttpsDomains;
static Vector<ResourceHandleContext*> s_sslBadcertResourceHandleContextList;
#if OS(ORBIS)
static Vector<ResourceHandleContext*> s_sslProcessedBadCertRHCList;
static WTF::Vector<ResourceHandleContext*> s_authProcessedContextList;
#define RHM_SELECT_PIPE_BUF 32
typedef enum {
    REQUEST_CANCEL = 0,
    PUMP_WORKERTHREAD,
    AUTHENTICATION,
    BAD_CERTIFICATE,
    MSG_LAST,
} WAKEUP_MSG;
static int s_RPIPE[4] = { 0, 0, 0, 0 };
static int s_WPIPE[4] = { 0, 0, 0, 0 };
static int s_maxPipeFd = 0;
static void restart(ResourceHandleContext* context)
{
    DEBUG_LOG("-> restart() url = [%s]\n", context->m_rawUrl);
    CURLMcode ret = curl_multi_add_handle(s_workerMultiHandle, context->m_handle);
    if (ret && ret != CURLM_CALL_MULTI_PERFORM && !(context->m_httpState & HttpState_IsFinishedContext))
        postHandleCancel(context);
}

// This function breaks select() in asynchronous operation.
static void wakeupSelect(int fd)
{
    ssize_t size = 0;
labelWritePipe:
    size = write(fd, "W", 1);
    if (size == -1) {
        int errsv = errno;
        switch (errsv) {
        case EAGAIN:
            goto labelWritePipe;
        case EPIPE:
            s_exit = true;
            DEBUG_LOG("### wakeupSelect EPIPE\n");
            ASSERT(false);
            break;
        }
    }
}
#endif

void ResourceHandleManager::handleBadCertificate(ResourceHandleContext* context)
{
    if (!context)
        return;

    DEBUG_LOG("### handleBadCertificate() url = [%s]\n", context->m_rawUrl);

    if (!s_userAcceptedHttpsDomains.contains(StringHash::hash(context->m_KURL.host()))) {
        if (context->m_isMainResource) {
            DEBUG_LOG("### handleBadCertificate() Main Resource\n");

            if (context->m_nwcontext) {
                if (context->m_chainForBadCertNotify)
                    postNotifyBadCertSynchronously(context);
            }
        }
    } else
        context->m_confirm = true;

    if (context->m_confirm) {
        s_userAcceptedHttpsDomains.add(StringHash::hash(context->m_KURL.host()));
        context->m_curlResult = CURLE_OK;

        WTF::MutexLocker workerMutexlock(*s_workerThreadMultiMutex);
#if OS(ORBIS)
        s_sslProcessedBadCertRHCList.append(reinterpret_cast<ResourceHandleContext*>(context));
        wakeupSelect(s_WPIPE[BAD_CERTIFICATE]);
#else
        resetSslConn(context);
        CURLMcode ret = curl_multi_add_handle(s_workerMultiHandle, context->m_handle);
        if (ret && ret != CURLM_CALL_MULTI_PERFORM)
            postHandleCancel(context);

        ResourceHandleManager::sharedInstance()->signalWorkerThread();
#endif
    } else
        postHandleCancel(context);
}

static void notifyAuthenticationChallenge(ResourceHandleContext* context, long AuthAvail, WebCore::AuthenticationChallenge& challenge, WebCore::ProtectionSpace& space)
{
    if (context->m_httpState & HttpState_HttpAuthModeFromHeader) {
        context->m_httpSpace = WebCore::ProtectionSpace(
            context->m_KURL.host(), 
            context->m_KURL.port(), 
            context->m_httpProtectType,
            context->m_httpRealm, 
            (AuthAvail == CURLAUTH_BASIC) ? ProtectionSpaceAuthenticationSchemeHTTPBasic : ProtectionSpaceAuthenticationSchemeHTTPDigest);
    }
    if (context->m_httpState & HttpState_ProxyAuthModeFromHeader) {
        context->m_proxySpace = WebCore::ProtectionSpace(
            context->m_proxyKURL.host(),
            context->m_proxyKURL.port(),
            context->m_proxyProtectType,
            context->m_proxyRealm, 
            (AuthAvail == CURLAUTH_BASIC) ? ProtectionSpaceAuthenticationSchemeHTTPBasic : ProtectionSpaceAuthenticationSchemeHTTPDigest);
    }

    // query application if username and password are valid.
    WebCore::Credential cred;
    WebCore::ResourceResponse resp;
    WebCore::ResourceError err;
    challenge = WebCore::AuthenticationChallenge(space, cred, 0, resp, err);
    if (context->m_nwcontext)
        context->m_nwcontext->onAuthenticationRequired(challenge);
}

static void getCredentialFromUserOrCache(
    ResourceHandleContext* context,
    long httpAvailAuth, // Basic or Digest
    String host, 
    int port, 
    ProtectionSpaceServerType type, 
    String realm, 
    ProtectionSpace& space,
    AuthenticationChallenge& challenge,
    CURLoption opt)
{
    DEBUG_LOG("->getCredentialFromUserOrCache() url = [%s]\n", context->m_rawUrl);

    String userpass("");

    space = ProtectionSpace(
        host, 
        port,
        type,
        realm,
        (httpAvailAuth == CURLAUTH_BASIC)?ProtectionSpaceAuthenticationSchemeHTTPBasic:ProtectionSpaceAuthenticationSchemeHTTPDigest);

    WebCore::Credential savedCredential = CredentialBackingStore::instance()->getLogin(space);

    if (!savedCredential.isEmpty()) {
        userpass.append(savedCredential.user());
        userpass.append(":");
        userpass.append(savedCredential.password());
        curl_easy_setopt(context->m_handle, (opt == CURLOPT_USERPWD)?CURLOPT_HTTPAUTH:CURLOPT_PROXYAUTH, httpAvailAuth);
        curl_easy_setopt(context->m_handle, opt, userpass.latin1().data());
        context->m_httpState |= (opt == CURLOPT_USERPWD)?HttpState_HasHttpCredential:HttpState_HasProxyCredential;
        challenge.m_user = savedCredential.user();
        challenge.m_pass = savedCredential.password();
        challenge.activate();
        context->m_httpState |= HttpState_ProxyUserPassWasSet;
    } else {
        if (!context->m_preSetProxyUserPass.isEmpty()) {
            userpass.append(context->m_preSetProxyUserPass);
            challenge.m_ok = true;
        } else {
            notifyAuthenticationChallenge(context, httpAvailAuth, challenge, space);

            userpass.append(challenge.m_user);
            userpass.append(":");
            userpass.append(challenge.m_pass);
        }

        if (challenge.m_ok) {
            curl_easy_setopt(context->m_handle, opt == CURLOPT_USERPWD?CURLOPT_HTTPAUTH:CURLOPT_PROXYAUTH, httpAvailAuth);
            curl_easy_setopt(context->m_handle, opt, userpass.latin1().data());
            context->m_httpState |= (opt == CURLOPT_USERPWD)?HttpState_HasHttpCredential:HttpState_HasProxyCredential;
            context->m_httpState |= HttpState_ProxyUserPassWasSet;
        } else
            context->m_httpState |= HttpState_AuthCancelled;
    }

    // reset realm status
    // maintain userpass cache
    if (!(context->m_httpState & HttpState_AuthCancelled)) {
        if (opt == CURLOPT_USERPWD) {
            context->m_httpState &= ~HttpState_HasRealmForHttpAuth;
            context->m_httpUserPass = userpass;
            context->m_httpState |= HttpState_HttpAuthInProcess;
        } else if (opt == CURLOPT_PROXYUSERPWD) {
            context->m_httpState &= ~HttpState_HasRealmForProxyAuth;
            context->m_proxyUserPass = userpass;
            context->m_httpState |= HttpState_ProxyAuthInProcess;
        }
    }
}

static void setAuthType(ResourceHandleContext* context)
{
    if (context->m_httpState & HttpState_HttpAuthModeFromHeader)
        curl_easy_getinfo(context->m_handle, CURLINFO_HTTPAUTH_AVAIL, &context->m_httpAuthType);
    if (context->m_httpState & HttpState_ProxyAuthModeFromHeader)
        curl_easy_getinfo(context->m_handle, CURLINFO_PROXYAUTH_AVAIL, &context->m_proxyAuthType);
    DEBUG_LOG("handleAuthentication() httpAuthAvail = [%ld] proxyAuthAvail = [%ld]\n", context->m_httpAuthType, context->m_proxyAuthType);
}

static void handleAuthentication(void* data)
{

    WebCore::ResourceHandleContext* context = reinterpret_cast<WebCore::ResourceHandleContext*>(data);

    DEBUG_LOG("### handleAuthentication() url = [%s]\n", context->m_rawUrl);

    // discard old curl handle.
    {
#if OS(PSP2)
        // curl_multi_remove_handle should be moved to RHM::workerThread.
        WTF::MutexLocker workerMutexlock(*s_workerThreadMultiMutex);
        curl_multi_remove_handle(s_workerMultiHandle, context->m_handle);
#endif
    }
    // create new curl handle.
    if (context->m_syncReq)
        ResourceHandleManager::sharedInstance()->initializeHandleForSync(context);

    if ((context->m_httpState & HttpState_IsHttps)
        && s_userAcceptedHttpsDomains.contains(StringHash::hash(context->m_KURL.host()))) {
        curl_easy_setopt(context->m_handle, CURLOPT_SSL_VERIFYHOST, false);
        curl_easy_setopt(context->m_handle, CURLOPT_SSL_VERIFYPEER, false);
    }

    if (context->m_httpState & HttpState_HttpAuthModeFromHeader)
        getCredentialFromUserOrCache(context, context->m_httpAuthType, context->m_KURL.host(), context->m_KURL.port(), context->m_httpProtectType, context->m_httpRealm, context->m_httpSpace, context->m_httpChallenge, CURLOPT_USERPWD); 

    if (context->m_httpState & HttpState_ProxyAuthModeFromHeader)
        getCredentialFromUserOrCache(context, context->m_proxyAuthType, context->m_proxyKURL.host(), context->m_proxyKURL.port(), context->m_proxyProtectType, context->m_proxyRealm, context->m_proxySpace, context->m_proxyChallenge, CURLOPT_PROXYUSERPWD);

    WTF::MutexLocker workerMutexlock(*s_workerThreadMultiMutex);
#if OS(ORBIS)
    s_authProcessedContextList.append(reinterpret_cast<ResourceHandleContext*>(context));
    wakeupSelect(s_WPIPE[AUTHENTICATION]);
#else
    CURLMcode ret = curl_multi_add_handle(s_workerMultiHandle, context->m_handle);
    if (ret && ret != CURLM_CALL_MULTI_PERFORM)
        postHandleCancel(context);

    ResourceHandleManager::sharedInstance()->signalWorkerThread();
#endif
}

static bool extractRealmFromAuthHeader(const String& header, long& httpAuthType, String& realm)
{
    if (header.isEmpty())
        return false;

    // Extract the auth scheme and realm from the header.
    const String strBasic("Basic");
    const String strDigest("Digest");

    int authTypeLen = 0;
    size_t authTypePos = header.find(strBasic);
    authTypeLen = strBasic.length();
    httpAuthType = CURLAUTH_BASIC;
    if (authTypePos == notFound) {
        authTypePos = header.find(strDigest);
        authTypeLen = strDigest.length();
        httpAuthType = CURLAUTH_DIGEST;
        if (authTypePos == notFound)
            return false;
    }

    size_t spacePos = header.find(' ');
    if (spacePos == notFound)
        return false;

    spacePos = header.find(' ', spacePos + 1); // find 2nd space which is located after auth type.
    if (spacePos == notFound)
        return false;


    size_t realmPos = header.findIgnoringCase("realm=", spacePos);
    if (realmPos == notFound)
        return false;

    size_t beginPos = realmPos + 6;
    realm  = header.right(header.length() - beginPos);
    if (realm.startsWith("\"")) {
        beginPos += 1;
        size_t endPos = header.find("\"", beginPos);
        if (endPos == notFound)
            return false;

        realm = header.substring(beginPos, endPos - beginPos);
    }

    return true;
}

void ResourceHandleManager::handleSslBadCertificate(void* context)
{
    WTF::MutexLocker userMutexlock(*s_verifySslThreadMutex);
    s_sslBadcertResourceHandleContextList.append(reinterpret_cast<ResourceHandleContext*>(context));

    s_verifySslThreadCondition->signal();
}

void ResourceHandleManager::verifySslThread(void* thrcontext)
{
    while (!s_exit) {
        {   WTF::MutexLocker lock(*s_verifySslThreadConditionMutex);
            s_verifySslThreadCondition->wait(*s_verifySslThreadConditionMutex);
            DEBUG_LOG("<- s_verifySslThreadSemaphore->wait() time = [%f]\n", currentTime());
        }

        bool continueBadCertHandling = true;
        do {
            WTF::MutexLocker lock(*s_verifySslThreadMutex);
            if (!s_sslBadcertResourceHandleContextList.isEmpty()) {
                ResourceHandleContext* context = s_sslBadcertResourceHandleContextList[0];
                s_sslBadcertResourceHandleContextList.remove(0);
                if (context)
                    handleBadCertificate(context);
            } else
                continueBadCertHandling = false;
        } while (continueBadCertHandling);
    }
}
///////////////////////////////////////////////////////////////////////////////////////////

static Mutex* sharedResourceMutex(curl_lock_data data)
{
    DEFINE_STATIC_LOCAL(Mutex, cookieMutex, ());
    DEFINE_STATIC_LOCAL(Mutex, dnsMutex, ());
    DEFINE_STATIC_LOCAL(Mutex, shareMutex, ());

    switch (data) {
    case CURL_LOCK_DATA_COOKIE:
        return &cookieMutex;
    case CURL_LOCK_DATA_DNS:
        return &dnsMutex;
    case CURL_LOCK_DATA_SHARE:
        return &shareMutex;
    default:
        ASSERT_NOT_REACHED();
        return 0;
    }
}

static void lockCurlSharedResource(curl_lock_data data)
{
    if (Mutex* mutex = sharedResourceMutex(data))
        mutex->lock();
}

static void unlockCurlSharedResource(curl_lock_data data)
{
    if (Mutex* mutex = sharedResourceMutex(data))
        mutex->unlock();
}

// libcurl does not implement its own thread synchronization primitives.
// these two functions provide mutexes for cookies, and for the global DNS
// cache.
static void curlLockCallback(CURL* handle, curl_lock_data data, curl_lock_access access, void* userPtr)
{
    lockCurlSharedResource(data);
}

static void curlUnlockCallback(CURL* handle, curl_lock_data data, void* userPtr)
{
    unlockCurlSharedResource(data);
}

void ResourceHandleManager::lockCurlSharedResource(curl_lock_data data)
{
    WebCore::lockCurlSharedResource(data);
}

void ResourceHandleManager::unlockCurlSharedResource(curl_lock_data data)
{
    WebCore::unlockCurlSharedResource(data);
}

static void networkStateChanged(bool isOnLine)
{
    networkStateNotifier().networkStateChange(isOnLine);
}

ResourceHandleManager::ResourceHandleManager()
{
    curl_global_init(CURL_GLOBAL_ALL);

    m_curlShareHandle = curl_share_init();
    curl_share_setopt(m_curlShareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
    curl_share_setopt(m_curlShareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
    curl_share_setopt(m_curlShareHandle, CURLSHOPT_LOCKFUNC, curlLockCallback);
    curl_share_setopt(m_curlShareHandle, CURLSHOPT_UNLOCKFUNC, curlUnlockCallback);

    memset((void*)m_cookieJarFileName, 0, sizeof(m_cookieJarFileName));
    Manx::NetworkProfile::get(Manx::NetPropsCookieFilename, m_cookieJarFileName, sizeof(m_cookieJarFileName));

    loadCookies();
    cleanupSessionCookies();
}

ResourceHandleManager::~ResourceHandleManager()
{
    curl_share_cleanup(m_curlShareHandle);
    curl_global_cleanup();
}

ResourceHandleManager* ResourceHandleManager::s_sharedInstance = 0;

void ResourceHandleManager::setup()
{
    s_setupMutex = new WTF::Mutex();
    ASSERT(s_setupMutex);

    MutexLocker locker(*s_setupMutex);
    s_sharedInstance = new ResourceHandleManager();
    ASSERT(s_sharedInstance);

    if (s_sharedInstance) {

        Manx::NetworkProfile::initialize();
        Manx::DNSPrefetch::initialize();
        Manx::Ssl::initialize();
        Manx::Ssl::SslContext::postNotifyOriginFrame = postNotifyOriginFrame;
        networkStateNotifier().networkStateChange(Manx::NetworkProfile::isOnLine());
        Manx::NetworkProfile::setOnLineStateCallback(networkStateChanged);

#if OS(ORBIS)
        // initialize pipe.
        for (int i = 0; i < 4; i++) {
            int pipeFds[2];
            if (!pipe(pipeFds)) {
                fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC);
                fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC);
                s_RPIPE[i] = pipeFds[0];
                if (s_maxPipeFd < s_RPIPE[i])
                    s_maxPipeFd = s_RPIPE[i];
                s_WPIPE[i] = pipeFds[1];
                if (s_maxPipeFd < s_WPIPE[i])
                    s_maxPipeFd = s_WPIPE[i];
                int flags = fcntl(s_RPIPE[i], F_GETFL, 0);
                fcntl(s_RPIPE[i], F_SETFL, flags | O_NONBLOCK);
                flags = fcntl(s_WPIPE[i], F_GETFL, 0);
                fcntl(s_WPIPE[i], F_SETFL, flags | O_NONBLOCK);
            } else
                ASSERT(false);
        }
        STDERR_LOG("### RHM MSG PIPES\n");
        STDERR_LOG("### cancel PIPE [%d]\n", s_RPIPE[0]);
        STDERR_LOG("### wakeup PIPE [%d]\n", s_RPIPE[1]);
        STDERR_LOG("### auth   PIPE [%d]\n", s_RPIPE[2]);
        STDERR_LOG("### ssl    PIPE [%d]\n", s_RPIPE[3]);
#endif

#if OS(ORBIS)
        s_notifyBadCertConditionMutex = new WTF::Mutex();
        ASSERT(s_notifyBadCertConditionMutex);
        s_notifyBadCertCondition = new WTF::ThreadCondition();
        ASSERT(s_notifyBadCertCondition);
#endif

        s_workerThreadMutex = new WTF::Mutex();
        ASSERT(s_workerThreadMutex);
        s_workerThreadMultiMutex = new WTF::Mutex();
        ASSERT(s_workerThreadMultiMutex);


        s_workerThreadConditionMutex = new WTF::Mutex();
#if USE(MANX_COND_INIT)
        s_workerThreadCondition = new WTF::ThreadCondition(*s_workerThreadConditionMutex);
#else
        s_workerThreadCondition = new WTF::ThreadCondition();
#endif
        s_workerThreadId = createThread(&workerThread, 0, "RscHdlMan:Worker");

        ASSERT(s_workerThreadId > 0);

        s_verifySslThreadMutex = new WTF::Mutex();
        ASSERT(s_verifySslThreadMutex);

        s_verifySslThreadConditionMutex = new WTF::Mutex();
#if USE(MANX_COND_INIT)
        s_verifySslThreadCondition = new WTF::ThreadCondition(*s_verifySslThreadConditionMutex);
#else
        s_verifySslThreadCondition = new WTF::ThreadCondition();
#endif
        s_verifySslThreadId = createThread(&verifySslThread, 0, "RscHdlMan:verifySslThread");

        ASSERT(s_verifySslThreadId > 0);

    } else
        ASSERT(false);

}

void ResourceHandleManager::cleanup()
{
    DEBUG_LOG("-> ResourceHandleManager::cleanup()\n");
    s_exit = true;

    if (s_workerThreadCondition) {
        s_workerThreadCondition->signal();
        WTF::waitForThreadCompletion(s_workerThreadId);
    }

    if (s_workerThreadMutex) { 
        delete s_workerThreadMutex; 
        s_workerThreadMutex = 0;
    }
    if (s_workerThreadConditionMutex) {
        delete s_workerThreadConditionMutex; 
        s_workerThreadConditionMutex = 0;
    }
    if (s_workerThreadCondition) {
        delete s_workerThreadCondition; 
        s_workerThreadCondition= 0;
    }
    if (s_verifySslThreadCondition) {
        s_verifySslThreadCondition->signal();
        WTF::waitForThreadCompletion(s_verifySslThreadId);
    }

    if (s_verifySslThreadMutex) { 
        delete s_verifySslThreadMutex; 
        s_verifySslThreadMutex = 0;
    }
    if (s_verifySslThreadConditionMutex) {
        delete s_verifySslThreadConditionMutex; 
        s_verifySslThreadConditionMutex = 0;
    }
    if (s_verifySslThreadCondition) {
        delete s_verifySslThreadCondition; 
        s_verifySslThreadCondition= 0;
    }

    if (s_filteringThreadId) {
        s_filteringThreadCondition->signal();
        WTF::waitForThreadCompletion(s_filteringThreadId);
    }

    if (s_filteringThreadMutex) { 
        delete s_filteringThreadMutex; 
        s_filteringThreadMutex = 0;
    }
    if (s_filteringThreadConditionMutex) {
        delete s_filteringThreadConditionMutex; 
        s_filteringThreadConditionMutex = 0;
    }
    if (s_filteringThreadCreatedConditionMutex) {
        delete s_filteringThreadCreatedConditionMutex; 
        s_filteringThreadCreatedConditionMutex = 0;
    }
    if (s_filteringThreadCondition) {
        delete s_filteringThreadCondition; 
        s_filteringThreadCondition= 0;
    }
    if (s_filteringThreadCreatedCondition) {
        delete s_filteringThreadCreatedCondition; 
        s_filteringThreadCreatedCondition = 0;
    }

    if (s_workerThreadMultiMutex) { 
        delete s_workerThreadMultiMutex; 
        s_workerThreadMultiMutex = 0;
    }

    if (s_setupMutex) {
        delete s_setupMutex;
        s_setupMutex = 0;
    }

#if OS(ORBIS)
    // cleanup pipe for monitoring select().
    for (int i = 0; i < 4; i++) {
        close(s_RPIPE[i]);
        close(s_WPIPE[i]);
    }
#endif
    if (s_sharedInstance) {
        delete s_sharedInstance;
        s_sharedInstance = 0;
    }

    Manx::Ssl::finalize();
    Manx::DNSPrefetch::finalize();
    Manx::NetworkProfile::finalize();
}

void ResourceHandleManager::selectSockets(CURLM* multi, long timeoutMilliSecond, int isFilterMode)
{
    ASSERT(multi);

    fd_set fdread;
    fd_set fdwrite;
    fd_set fdexcep;
    int maxfd = 0;

    struct timeval timeout;
    timeout.tv_sec = timeoutMilliSecond / 1000;
    timeout.tv_usec = (timeoutMilliSecond % 1000) * 1000; // select waits microseconds

    int rc = 0;
    do {
        FD_ZERO(&fdread);
        FD_ZERO(&fdwrite);
        FD_ZERO(&fdexcep);
        curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd);
        STDERR_LOG("<- curl_multi_fdset() maxfd = [%d] time = [%f]\n", maxfd, currentTime());
#if OS(PSP2)
        if (maxfd >= 0) {
#endif
#if OS(ORBIS)
            for (int i = 0; i < 4; i++)
                FD_SET(s_RPIPE[i], &fdread); // cancel,add,auth,badcert
            if (maxfd < s_maxPipeFd)
                maxfd = s_maxPipeFd;
#endif

            if (timeoutMilliSecond > 0)
                rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
#if OS(ORBIS)
            else {
                STDERR_LOG("-> select() infinite \n");
                rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, 0);
            }
            if (rc > 0) {
                char buff[RHM_SELECT_PIPE_BUF];
                memset(buff, 0x0, RHM_SELECT_PIPE_BUF);
                ssize_t msgNum = 0;
                int savedErrno = 0;
                for (int i = 0; i < MSG_LAST; i++) {
                    if (FD_ISSET(s_RPIPE[i], &fdread)) {
labelRetryRead:
                        msgNum = read(s_RPIPE[i], buff, RHM_SELECT_PIPE_BUF);
                        if (msgNum == -1) {
                            savedErrno = errno;
                            if (savedErrno == EAGAIN || savedErrno == EWOULDBLOCK) {
                                DEBUG_LOG("<- pipe EAGAIN\n");
                                goto labelRetryRead;
                            }
                            STDERR_LOG("selectSockets read() return -1: errno = [%d]\n", savedErrno);
                            ASSERT(false);
                        }
                        if (!msgNum) {
                            // pipe is closed.
                            // consider it is a shutting down.
                            STDERR_LOG("read msgNum == 0 pipe is broken?\n");
                            s_exit = true;
                            ASSERT(false);
                            break;
                        }
                        if (msgNum > 0) {
                            int ctxNum = 0;
                            WTF::MutexLocker workerMultiMutexLock(*s_workerThreadMultiMutex);
                            switch (i) {
                            case REQUEST_CANCEL:
                                STDERR_LOG("<- cancelled [%s]\n", buff);
                                // check cancel
                                ctxNum = s_cancelContextList.size();
                                for (int i = 0; i < ctxNum; i++) {
                                    ResourceHandleContext* context = s_cancelContextList[0];
                                    s_cancelContextList.remove(0);
                                    if (context)
                                        context->m_httpState |= HttpState_Cancelled;
                                }
                                break;
                            case AUTHENTICATION:
                                ctxNum = s_authProcessedContextList.size();
                                for (int i = 0; i < ctxNum; i++) {
                                    ResourceHandleContext* context = s_authProcessedContextList[0];
                                    s_authProcessedContextList.remove(0);
                                    restart(context);
                                }
                                break;
                            case BAD_CERTIFICATE:
                                if (!s_sslProcessedBadCertRHCList.isEmpty()) {
                                    ResourceHandleContext* context = s_sslProcessedBadCertRHCList[0];
                                    s_sslProcessedBadCertRHCList.remove(0);
                                    resetSslConn(context);
                                    restart(context);
                                }
                                break;
                            }
                        }
                    }
                }
            }
#endif
            STDERR_LOG("<- ::select() rc = [%d] errno = [%d] time = [%f]\n", rc, errno, currentTime());
#if OS(PSP2)
        } else {
            if (isFilterMode) {
                WTF::MutexLocker lock(*s_filteringThreadConditionMutex);
                s_filteringThreadCondition->timedWait(*s_filteringThreadConditionMutex, currentTime() + (16.6 / 1000));
            } else {
                // [v:Bug 85719]
                // If fd is negative (-1), it should wait for 16.6 milliseconds or signal.
                WTF::MutexLocker lock(*s_workerThreadConditionMutex);
                s_workerThreadCondition->timedWait(*s_workerThreadConditionMutex, currentTime() + (16.6 / 1000));
            }
        }
#endif
    } while (rc == -1 && errno == EINTR);
}

#if OS(ORBIS)
void ResourceHandleManager::selectSocketsForFiltering(CURLM* multi, long timeoutMilliSecond)
{
    ASSERT(multi);

    fd_set fdread;
    fd_set fdwrite;
    fd_set fdexcep;
    int maxfd = 0;

    struct timeval timeout;
    timeout.tv_sec = timeoutMilliSecond / 1000;
    timeout.tv_usec = (timeoutMilliSecond % 1000) * 1000; // select waits microseconds

    int rc = 0;
    do {
        FD_ZERO(&fdread);
        FD_ZERO(&fdwrite);
        FD_ZERO(&fdexcep);
        curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd);
        DEBUG_LOG("<- curl_multi_fdset() maxfd = [%d] time = [%f]\n", maxfd, currentTime());
        if (maxfd >= 0) {
            if (timeoutMilliSecond > 0)
                rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
            DEBUG_LOG("<- ::select() time = [%f]\n", currentTime());
        }
    } while (rc == -1 && errno == EINTR);

}
#endif

void ResourceHandleManager::filteringThread(void* context)
{
#if OS(PSP2)
    s_filteringThreadCreated = true;
#endif

    DEBUG_LOG("-> ResourceHandleManager::filteringThread()\n");
    s_filteringMultiHandle = curl_multi_init();

    curl_multi_setopt(s_filteringMultiHandle, CURLMOPT_MAXCONNECTS, 32);

    bool performCurlRequest = false;

    while (!s_exit) {
#if OS(ORBIS)
        if (!performCurlRequest) {
            DEBUG_LOG("-> s_filteringThreadCondition->wait() time = [%f]\n", currentTime());
            waitFilteringThread();
            DEBUG_LOG("<- s_filteringThreadCondition->wait() time = [%f]\n", currentTime());
        }
#endif
        performCurlRequest = ResourceHandleManager::s_sharedInstance->processFiltering();

        long timeout = 0;
        if (CURLM_OK != curl_multi_timeout(s_filteringMultiHandle, &timeout))
            ASSERT(false);
        // curl_multi_timeout returns CURLM_OK or CURLM_BAD_HANDLE.
#if OS(PSP2)
        long timeoutAtMost = -1;
        curl_multi_timeout(s_workerMultiHandle, &timeoutAtMost);
        if ((-1 == timeoutAtMost) || (SELECT_TIMEOUT_VALUE < timeoutAtMost))
            timeout = SELECT_TIMEOUT_VALUE;
#endif
        DEBUG_LOG("curl_multi_timeout timeout time value = %ld\n", timeout);
        
        if (!timeout) {
            performCurlRequest = true;
            continue;
        }

#if OS(ORBIS)
        selectSocketsForFiltering(s_filteringMultiHandle, timeout);
#else
        selectSockets(s_filteringMultiHandle, timeout, 1);
#endif
    }

    curl_multi_cleanup(s_filteringMultiHandle);
}

void ResourceHandleManager::waitWorkerThreadMessage(bool performCurlRequest, long& timeout)
{
/*
#if OS(PSP2)
    // [v:Bug 85719]
    // FIXME: This wating thread control should be replaced with an effecient socket controll process.
    if (!performCurlRequest) {
        WTF::MutexLocker lock(*s_workerThreadConditionMutex);
        s_workerThreadCondition->wait(*s_workerThreadConditionMutex);
        timeout = 0;
    }
#endif
*/
    // 1. In case timeout is -1, there are no sockets ready to read and write,
    //    selectSockets() will wait indefinitely
    //    until any of sockets ready to read or write.
    // 2. In case timeout is greater than 0, there are some running requests in
    //    curl Multi handle and the timeout is remaining time to the nearest timeout
    //    in all running request. The timeout is used in select() in selectSockets().
    // 3. In case timeout is 0, it is necessary to call curl_multi_perform() immediately,
    //    so skip calling selectSockets() here.
    if (timeout)
        selectSockets(s_workerMultiHandle, timeout, 0);
}

void ResourceHandleManager::workerThread(void* argument)
{
#ifndef NDEBUG
    workerThreadIdentifier = WTF::currentThread();
#endif
    s_workerMultiHandle = curl_multi_init();

    curl_multi_setopt(s_workerMultiHandle, CURLMOPT_MAXCONNECTS, 160);
#if USE_HTTP_PIPELINE
    curl_multi_setopt(s_workerMultiHandle, CURLMOPT_PIPELINING, 1);
#endif
#if LIBCURL_VERSION_NUM >= 0x071E00 // After libcurl 7.30.0
    curl_multi_setopt(s_workerMultiHandle, CURLMOPT_MAX_TOTAL_CONNECTIONS, 160);
    curl_multi_setopt(s_workerMultiHandle, CURLMOPT_MAX_HOST_CONNECTIONS, Manx::NetworkProfile::get(Manx::NetPropsMaximumHTTPConnectionCountPerHost));
#if USE_HTTP_PIPELINE
    curl_multi_setopt(s_workerMultiHandle, CURLMOPT_MAX_PIPELINE_LENGTH, 6);
#endif
#endif

    bool performCurlRequest = false;

    long timeout = SELECT_TIMEOUT_VALUE;
    while (!s_exit) {
        waitWorkerThreadMessage(performCurlRequest, timeout);
        ResourceHandleManager::s_sharedInstance->startScheduledJobs();
        DEBUG_LOG("-> processDownload(%s) time = [%f]\n", performCurlRequest?"true":"false", currentTime());
        performCurlRequest = ResourceHandleManager::s_sharedInstance->processDownload();
        DEBUG_LOG("<- processDownload() time = [%f]\n", currentTime());
        // 1. In case performCurlRequest is true, it is necessary to skip
        //    selectSockets() and to call curl_multi_perform() 
        //    in processDownload() immediately.
        // 2. In case performCurlRequest is false, it is necessary to call
        //    curl_multi_timeout() to get the remaining time to the nearest timeout
        //    in all running requests which select() will use for timeout
        //    in selectSockets().

        if (performCurlRequest) {
#if OS(PSP2)
            // [v:Bug 85719]
            // "SELECT_TIMEOUT_VALUE" is a magic waiting time value as WORKWROUND.
            // The value is used when CURL requires wait to receive data from network.
            // To pump processDownlaod(), the magic value is used as a waiting time for select().
            long timeoutAtMost = -1;
            curl_multi_timeout(s_workerMultiHandle, &timeoutAtMost);
            if ((-1 == timeoutAtMost) || (SELECT_TIMEOUT_VALUE < timeoutAtMost))
                timeout = SELECT_TIMEOUT_VALUE;
#else
            // curl_multi_timeout returns CURLM_OK or CURLM_BAD_HANDLE.
            if (CURLM_OK != curl_multi_timeout(s_workerMultiHandle, &timeout))
                ASSERT(false);
            // In case timeout is 0, it is necessary to skip calling select()
            // and to call processDownload(curl_multi_perform) immediately.
            STDERR_LOG("curl_multi_timeout timeout time value = %ld\n", timeout);
#endif
        } else
            timeout = SELECT_TIMEOUT_VALUE;
    }

    curl_multi_cleanup(s_workerMultiHandle);
}

void ResourceHandleManager::waitFilteringThread()
{
    WTF::MutexLocker lock(*s_filteringThreadConditionMutex);
    if (!s_filteringThreadCreated) {
        WTF::MutexLocker lock(*s_filteringThreadCreatedConditionMutex);
        s_filteringThreadCreatedCondition->signal();
        s_filteringThreadCreated = true;
    }
    s_filteringThreadCondition->wait(*s_filteringThreadConditionMutex);
}

void ResourceHandleManager::createFilteringThread(WTF::ThreadFunction threadfunc, void* ctxt, const char* threadName)
{
    s_filteringThreadConditionMutex = new WTF::Mutex();
#if USE(MANX_COND_INIT)
    s_filteringThreadCondition = new WTF::ThreadCondition(*s_filteringThreadConditionMutex);
#else
    s_filteringThreadCondition = new WTF::ThreadCondition();
#endif

    s_filteringThreadCreatedConditionMutex = new WTF::Mutex();
#if USE(MANX_COND_INIT)
    s_filteringThreadCreatedCondition = new WTF::ThreadCondition(*s_filteringThreadCreatedConditionMutex);
#else
    s_filteringThreadCreatedCondition = new WTF::ThreadCondition();
#endif

    ASSERT(s_filteringThreadConditionMutex && s_filteringThreadCondition && s_filteringThreadCreatedConditionMutex && s_filteringThreadCreatedCondition);

    {
        WTF::MutexLocker lock(*s_filteringThreadCreatedConditionMutex);
        s_filteringThreadId = createThread(threadfunc, reinterpret_cast<void*>(s_filteringThreadCondition), threadName);
#if OS(ORBIS)
        s_filteringThreadCreatedCondition->wait(*s_filteringThreadCreatedConditionMutex);
#endif
    }
}

void ResourceHandleManager::checkAndCreateFilteringThread()
{
    if (!s_filteringThreadId) {

        s_filteringThreadMutex = new WTF::Mutex();
        ASSERT(s_filteringThreadMutex);

        createFilteringThread(&filteringThread, 0, "RscHdlMan:Filtering");
    }
}

ResourceHandleManager* ResourceHandleManager::sharedInstance()
{
    if (!s_initialized) {
        MutexLocker locker(*s_setupMutex);
        s_initialized = true;
    }

    ASSERT(s_sharedInstance);

    if (Manx::WebSecurity::activatedStatus())
        s_sharedInstance->checkAndCreateFilteringThread();

    return s_sharedInstance;
}


/*
  Functions which post to main thread

  - postDidRecvData
  - postDidRecvResponse
  - postWillSendRequest
  - postHandleCancel
  - postDidFinish
  - postDidFail
  - postHandleResponseHeaders
  - postNotifyOriginFrame

 */
void postDidRecvData(ResourceHandleContext* context, void* ptr, const char* url, size_t totalSize)
{
    ResourceHandleCommand* cmd = new ResourceHandleCommand();
    ASSERT(cmd);
    cmd->m_data[0].typeVoidPtr = context->job;
    cmd->m_data[1].typeCharPtr = new char[totalSize];
    ASSERT(cmd->m_data[1].typeCharPtr);
    memcpy(cmd->m_data[1].typeCharPtr, ptr, totalSize);
    cmd->m_data[2].typeInt = totalSize;

    size_t urllen = strlen(url);
    cmd->m_data[3].typeCharPtr = new char[urllen + 1];
    ASSERT(cmd->m_data[3].typeCharPtr);
    strncpy(cmd->m_data[3].typeCharPtr, url, urllen);
    cmd->m_data[3].typeCharPtr[urllen] = '\0';

    callOnMainThread(didRecvData, cmd);
}

void postDidRecvResponse(ResourceHandleContext* context)
{
    char* url = 0;
    if (CURLE_OK == curl_easy_getinfo(context->m_handle, CURLINFO_EFFECTIVE_URL, &url)) {
        ResourceHandleCommand* cmd = new ResourceHandleCommand();
        ASSERT(cmd);
        cmd->m_data[0].typeVoidPtr = reinterpret_cast<void*>(context->job);
        if (url) {
            size_t urllen = strlen(url);
            cmd->m_data[1].typeCharPtr = new char[urllen + 1];
            ASSERT(cmd->m_data[1].typeCharPtr);
            strncpy(cmd->m_data[1].typeCharPtr, url, urllen);
            cmd->m_data[1].typeCharPtr[urllen] = '\0';
        }

        // m_evaluatedkurl is used to show a certain infomation page in URL filtering.
        if (!context->m_evaluatedkurl.isEmpty()) {
            size_t evaluatedurllen = strlen(context->m_evaluatedkurl.string().latin1().data());
            cmd->m_data[2].typeCharPtr = new char[evaluatedurllen + 1];
            ASSERT(cmd->m_data[2].typeCharPtr);
            strncpy(cmd->m_data[2].typeCharPtr, context->m_evaluatedkurl.string().latin1().data(), evaluatedurllen);
            cmd->m_data[2].typeCharPtr[evaluatedurllen] = '\0';
        }
        callOnMainThread(didRecvResponse, cmd);
        callOnMainThread(replaceUrlOfMainResource, context);
    }
}

#if OS(ORBIS)
void postWillFilterSendRequest(ResourceHandleContext* context)
{
    ResourceHandleCommand* cmd = new ResourceHandleCommand();
    ASSERT(cmd);
    cmd->m_data[0].typeVoidPtr = reinterpret_cast<void*>(context->job);
    cmd->m_data[1].typeVoidPtr = context;
    callOnMainThread(willFilterSendRequest, cmd);
}
#endif

void postWillSendRequest(ResourceHandleContext* context)
{
    ResourceHandleCommand* cmd = new ResourceHandleCommand();
    ASSERT(cmd);
    cmd->m_data[0].typeVoidPtr = reinterpret_cast<void*>(context->job);
    cmd->m_data[1].typeVoidPtr = context;
    callOnMainThread(willSendRequest, cmd);
}

void postHandleCancel(ResourceHandleContext* context)
{
    ResourceHandleCommand* cmd = new ResourceHandleCommand();
    ASSERT(cmd);
    cmd->m_data[0].typeVoidPtr = context->job;
    cmd->m_data[1].typeVoidPtr = context;
    callOnMainThread(handleCancel, cmd);
}

void postDidFinish(ResourceHandleContext* context)
{
#if OS(ORBIS)
    {
        WTF::MutexLocker lock(*s_workerThreadMultiMutex);
        s_jobToContextMap.remove(context->job);
    }
#endif
    ResourceHandleCommand* cmd = new ResourceHandleCommand();
    ASSERT(cmd);
    size_t urllen = strlen(context->m_rawUrl);
    cmd->m_data[0].typeVoidPtr = reinterpret_cast<void*>(context->job);
    cmd->m_data[1].typeCharPtr = new char[urllen + 1];
    ASSERT(cmd->m_data[1].typeCharPtr);
    strncpy(cmd->m_data[1].typeCharPtr, context->m_rawUrl, urllen);
    cmd->m_data[1].typeCharPtr[urllen] = '\0';
    DEBUG_LOG("postDidFinish [%s]\n", context->m_rawUrl);
    cmd->m_data[2].typeVoidPtr = context;
    callOnMainThread(didFinish, cmd);
}

void postDidFail(ResourceHandleContext* context)
{
#if OS(ORBIS)
    {
        WTF::MutexLocker lock(*s_workerThreadMultiMutex);
        s_jobToContextMap.remove(context->job);
    }
#endif
    ResourceHandleCommand* cmd = new ResourceHandleCommand();
    ASSERT(cmd);
    cmd->m_data[0].typeVoidPtr = reinterpret_cast<void*>(context->job);
    cmd->m_data[1].typeInt = context->m_curlResult;

    if (context->m_rawUrl) {
        size_t urllen = strlen(context->m_rawUrl);
        cmd->m_data[2].typeCharPtr = new char[urllen + 1];
        ASSERT(cmd->m_data[2].typeCharPtr);
        strncpy(cmd->m_data[2].typeCharPtr, context->m_rawUrl, urllen);
        cmd->m_data[2].typeCharPtr[urllen] = '\0';
        DEBUG_LOG("postDidFail [%s]\n", context->m_rawUrl);
    }

    size_t errlen = strlen(curl_easy_strerror(context->m_curlResult));
    cmd->m_data[3].typeCharPtr = new char[errlen + 1];
    ASSERT(cmd->m_data[3].typeCharPtr);
    strncpy(cmd->m_data[3].typeCharPtr, curl_easy_strerror(context->m_curlResult), errlen);
    cmd->m_data[4].typeVoidPtr = context;

    callOnMainThread(didFail, cmd);
}

void postHandleResponseHeaders(ResourceHandleContext* context)
{
    CURL* h = context->m_handle;

    double contentLength = 0;
    curl_easy_getinfo(h, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);

    char* url = 0;
    curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &url);

    long httpCode = 0;
    curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode);

    ResourceHandleCommand* cmd = new ResourceHandleCommand();
    ASSERT(cmd);
    size_t urllen = strlen(url);
    cmd->m_data[0].typeVoidPtr = reinterpret_cast<void*>(context->job);
    cmd->m_data[1].typeLongLongInt = static_cast<long long int>(contentLength);
    cmd->m_data[2].typeCharPtr = new char[urllen + 1];
    ASSERT(cmd->m_data[2].typeCharPtr);
    strncpy(cmd->m_data[2].typeCharPtr, url, urllen);
    cmd->m_data[2].typeCharPtr[urllen] = '\0';
    cmd->m_data[3].typeInt = httpCode;
    cmd->m_data[4].typeCharPtr = new char[context->m_respHeadersStr.length() + 1];
    ASSERT(cmd->m_data[4].typeCharPtr);
    strncpy(cmd->m_data[4].typeCharPtr, context->m_respHeadersStr.utf8().data(), context->m_respHeadersStr.length());
    cmd->m_data[4].typeCharPtr[context->m_respHeadersStr.length()] = '\0';

    callOnMainThread(handleResponseHeaders, cmd);
}

void postNotifyOriginFrame(void* context)
{
    ResourceHandleCommand* cmd = new ResourceHandleCommand();
    ASSERT(cmd);
    cmd->m_data[0].typeVoidPtr = context;
    callOnMainThread(notifyOriginFrame, cmd);
}

void ResourceHandleManager::postNotifyBadCertSynchronously(ResourceHandleContext* context)
{
    ASSERT(context);

#if OS(PSP2)
    ResourceError error(context->m_sslError, String());
    error.setCert(context->m_chainForBadCertNotify);
    context->m_nwcontext->onCertificateVerificationRequest(error);
    context->m_confirm = error.confirmCert();
#else
    ResourceHandleCommand* cmd = new ResourceHandleCommand();
    ASSERT(cmd);
    cmd->m_data[0].typeVoidPtr = reinterpret_cast<void*>(context);
    cmd->m_data[1].typeInt = context->m_sslError;
    if (context->m_rawUrl) {
        size_t urllen = strlen(context->m_rawUrl);
        cmd->m_data[2].typeCharPtr = new char[urllen + 1];
        ASSERT(cmd->m_data[2].typeCharPtr);
        strncpy(cmd->m_data[2].typeCharPtr, context->m_rawUrl, urllen);
        cmd->m_data[2].typeCharPtr[urllen] = '\0';
        DEBUG_LOG("postNotifyBadCertSynchronously [%s]\n", context->m_rawUrl);
    }
    WTF::MutexLocker locker(*s_notifyBadCertConditionMutex);
    callOnMainThread(notifyBadCertSynchronously, cmd);
    s_notifyBadCertCondition->wait(*s_notifyBadCertConditionMutex);
#endif
}

void saveResponseHeader(ResourceHandleContext* context, char* ptr, size_t totalSize)
{
    context->m_respHeadersStr.append(String(ptr, totalSize));
    context->m_respHeadersStr.append(String("\r\n"));
}

void extractAuthInfo(ResourceHandleContext* context, String& header, CURL* handle)
{
    ASSERT(context && handle);

    DEBUG_LOG("### extractAuthInfo()");
    DEBUG_LOG("    url = [%s]\n", context->m_rawUrl);
    DEBUG_LOG("    header = [%s]\n", header.latin1().data());

    if ((!(context->m_httpState & HttpState_HasRealmForHttpAuth)
        || !(context->m_httpState & HttpState_HasRealmForProxyAuth))
        && (header.find((const LChar*)"Authenticate") != -1)) {
        // To handle "WWW" or "Proxy" Authenticate,
        // this browser has to retrieve "realm" from
        // Authenticate header in this libcurl header callback.
        // "realm" value gotten in this method will be used in
        // handleAuthentication() function afterwards to show in
        // authenticate dialog.
        long httpCode = 0;
        long connectCode = 0;
        curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &httpCode);
        curl_easy_getinfo(handle, CURLINFO_HTTP_CONNECTCODE, &connectCode);

        if (httpCode == 401) {
            if (context->m_httpState & HttpState_IsHttps)
                context->m_httpProtectType = ProtectionSpaceServerHTTPS;
            else 
                context->m_httpProtectType = ProtectionSpaceServerHTTP;

            if (extractRealmFromAuthHeader(header, context->m_httpAuthType, context->m_httpRealm))
                context->m_httpState |= HttpState_HasRealmForHttpAuth;
            else
                context->m_httpState &= ~HttpState_HasRealmForHttpAuth;
            context->m_httpState |= HttpState_HttpAuthModeFromHeader;
        } else if (httpCode == 407 || connectCode == 407) {
            context->m_proxyProtectType = ProtectionSpaceProxyHTTP;
            if (extractRealmFromAuthHeader(header, context->m_proxyAuthType, context->m_proxyRealm))
                context->m_httpState |= HttpState_HasRealmForProxyAuth;
            else
                context->m_httpState &= ~HttpState_HasRealmForProxyAuth;
            context->m_httpState |= HttpState_ProxyAuthModeFromHeader;
        }
    }
}

/*
  Functions registered to curl handle which are called in worker thread.
  Classes in WebCore were removed to avoid race condition with main thread.

  - headerCallbackAsync
  - writeCallbackAsync
  - readCallbackAsync

*/
size_t ResourceHandleManager::headerCallbackAsync(char* ptr, size_t size, size_t nmemb, void* data)
{
    ASSERT(data);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(data);
    if (context->m_httpState & HttpState_Cancelled)
        return 0;

    // This should never be called from multi_perform when deferred loading is activated.
    // Because this curl handle is paused.
    ASSERT(!(context->m_httpState & HttpState_DefersLoading));

    long connectCode = 0;
    long httpCode = 0;
    curl_easy_getinfo(context->m_handle, CURLINFO_HTTP_CONNECTCODE, &connectCode);
    curl_easy_getinfo(context->m_handle, CURLINFO_RESPONSE_CODE, &httpCode);

    if (!httpCode && connectCode == 200) {
        if (context->m_httpState & HttpState_ProxyAuthInProcess)
            saveCredential(context, context->m_proxyChallenge, context->m_proxySpace, context->m_proxyKURL);

        return size * nmemb;
    }

    if ((context->m_httpState & HttpState_IsPost) && !(context->m_httpState & HttpState_RedirectedAfterPost)) {
#if 0
        // Explanation of each 3xx status codes.
        switch (httpCode) {
        case 300:
            // Server proposes multiple representations of resource to the user-agent.
            // User or User-Agent can select a preferred representation.
            // If server has a preferred choice of representation, 
            // it will be included in "Location:" header and user-agent may use
            // for automatic redirection.
            // So, from user-agent's point of view, if there is a location header
            // with 300, it will redirect using the location header's url.
            // There are no definitions about HTTP method changing.
            break;
        case 301:
            // On response other than GET or HEAD, the user-agent MUST NOT
            // automatically redirect the request unless it can be
            // confirmed by the user.
            // Existing HTTP/1.0 user-agents will erroneously change
            // it into a GET reqeust.
            break;
        case 302:
            // RFC 1945 and 2068 cite that the user-agent MUST NOT change
            // the request's method which is redirecting, but
            // many user-agents looks 302 response same as 303 response and
            // change to GET method instead of an original request method.
            // Status code 303 and 307 were added for server that wants to 
            // clarify the user-agent's behavior.

            // Change method to GET always.
            break;
        case 303:
            // According to RFC 2616 section 10.3.4, 
            // the user-agent SHOULD get this resource using GET method.
            break;
        case 305:
            // The user-agent should use Location's value as Proxy server.
            break;
        case 306:
            // Unused.
            break;
        case 307:
            // According to RFC 2615 section 10.3.8,
            // status code 307 has an original meaning of 302.
            // That is, the user-agent MUST NOT change HTTP method.
            break;
        case 308:
            // This status code is currently being supposed in 
            // new Internet Draft.
            // This Status code defines a missing variant for 
            // permanent redirection which doesn't permit
            // HTTP method changing.
            // +----------------------+-----------+---------+
            // |                      |Permanently|Temporary|
            // +----------------------+-----------+---------+
            // |Allow Method Changing |301        |302(303) |
            // +----------------------+-----------+---------+
            // |Deny  Method Changing |308        |307      |
            // +----------------------+-----------+---------+
            break;
        }
#endif
        // To prevent WebCore confising by telling status code 100,
        // return nothing.
        // FIXME: consider the case of 101 and 102.
        if (httpCode == 100)
            return size * nmemb;
    }

    size_t totalSize = size * nmemb;

    String header(static_cast<const char*>(ptr), totalSize);

    extractAuthInfo(context, header, context->m_handle);

    if (header == "\r\n" || header == "\n") {
        postHandleResponseHeaders(context);

        if (httpCode == 200)
            context->m_httpState |= HttpState_IsFinishedContext;

        if (httpCode == 401 || httpCode == 407 || connectCode == 407)
            return prepareHandleAuthenticate(context, httpCode, connectCode, totalSize, data);

        // HTTP redirection
        if (300 <= httpCode && httpCode < 400) {

            // FILTER mode
            if (context->m_filteringActivatedStatus && (context->m_httpState & HttpState_HasLocation)) {
                bool skipEval = false;
                convertToFilteringUrl(context, skipEval);
                if (skipEval) {
                    if (!context->m_evaluatedkurl.isEmpty())
                        context->m_evaluatedkurl = context->m_KURL;
                    postWillSendRequest(context);
                    return totalSize;
                }
                ResourceHandleManager::sharedInstance()->initializeHandleForFiltering(context);
                context->m_filteringRespHeaders.clear();
                context->m_filteringBody.clear();
                CURLcode err = curl_easy_perform(context->m_filteringhandle);
                curl_easy_cleanup(context->m_filteringhandle);
                if (!err) {
                    bool showPage = false;
                    evaluateFilteringResult(context, showPage);
                    if (showPage) {
                        if (context->m_KURL == context->m_evaluatedkurl) {
                            postWillSendRequest(context);
                            return totalSize;
                        }
                        // make this request failure and will goto block page.
                        context->m_blockedLocation = true;
#if OS(ORBIS)
                        postWillFilterSendRequest(context);
#endif
                    }
                }
                // network error occurred
                return 0;
            }

            // Normal mode
            double contentLength = 0;
            curl_easy_getinfo(context->m_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
            switch (httpCode) {
            case 305: // HTTP Code: Use Proxy
            case 306: // (Not Used: Reserved)
            case 399: // FIXME: This code check is needed to follow other browsers behavior.
                if ((context->m_httpState & HttpState_HasLocation) && !contentLength)
                    return 0;
            default:
                postWillSendRequest(context);
                return totalSize;
            }
        }

        postDidRecvResponse(context);

    } else {

        int splitPos = header.find(':');
        if (splitPos != -1) {
            if (header.lower().startsWith("location:")) {
                KURL redirURL = KURL(context->m_KURL, header.substring(splitPos + 1).stripWhiteSpace());
                if (!redirURL.isEmpty()) {
                    // See also:
                    //   http://www.w3.org/Protocols/HTTP/Fragment/draft-bos-http-redirect-00.txt
                    //   https://bugs.webkit.org/show_bug.cgi?id=24175
                    if (redirURL.protocolIsInHTTPFamily() && !redirURL.hasFragmentIdentifier() && context->m_KURL.hasFragmentIdentifier())
                        redirURL.setFragmentIdentifier(context->m_KURL.fragmentIdentifier());
                    context->m_KURL = redirURL;
                }
                context->m_httpState |= HttpState_HasLocation;
            }
#ifdef DISABLE_X_FRAME_OPTIONS // disable x-frame-options for debug
            String xFrameOptions = response.httpHeaderField("x-frame-options");
            if (!xFrameOptions.isEmpty())
                return totalSize;
#endif
        }

        saveResponseHeader(context, ptr, totalSize);
    }

    return totalSize;
}

size_t ResourceHandleManager::writeCallbackAsync(char* ptr, size_t size, size_t nmemb, void* data)
{
    ASSERT(data);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(data);

    if (context->m_httpState & HttpState_Cancelled)
        return 0;

    // This should never be called from multi_perform when deferred loading is activated.
    // Because this curl handle is paused.
    ASSERT(!(context->m_httpState & HttpState_DefersLoading));

    size_t totalSize = size * nmemb;
    long httpCode = 0;
    long connectCode = 0;
    CURLcode err = CURLE_OK;

    err = curl_easy_getinfo(context->m_handle, CURLINFO_RESPONSE_CODE, &httpCode);
    if (CURLE_OK != err)
        goto doPostRecvData; // Error Page???
    err = curl_easy_getinfo(context->m_handle, CURLINFO_HTTP_CONNECTCODE, &connectCode);
    if (CURLE_OK != err) 
        goto doPostRecvData; // Error Page???

    if ((300 <= httpCode) && (httpCode < 400)) // Redirection
        goto doPostRecvData;
    
    if (((httpCode == 401) || (httpCode == 407) || (connectCode == 407)) // Auth Case
        && (context->m_httpState & (HttpState_HttpAuthInProcess | HttpState_ProxyAuthInProcess))
        && !(context->m_httpState & HttpState_AuthCancelled))
        return totalSize;

doPostRecvData:
    char* url = 0;
    if (CURLE_OK != curl_easy_getinfo(context->m_handle, CURLINFO_EFFECTIVE_URL, &url))
        ASSERT(false);
    postDidRecvData(context, ptr, url, totalSize);

    return totalSize;
}

size_t ResourceHandleManager::readCallbackAsync(void* ptr, size_t size, size_t nmemb, void* data)
{
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(data);
    ASSERT(context);

    size_t sent = 0; 
    if ((context->m_httpState & HttpState_Cancelled)
        || !size || !nmemb
        || !(context->m_formDataStream.hasMoreElementsAsync(context->m_formData)?context->m_formDataStream.readAsync(ptr, size, nmemb, context->m_formData, sent):true))
        return CURL_READFUNC_ABORT;

    // This should never be called from multi_perform when deferred loading is activated.
    // Because this curl handle is paused.
    ASSERT(!(context->m_httpState & HttpState_DefersLoading));

    return sent;
}

// FIXME: ioctlCallback() and ioctlCallbackAsync() can be gathered into one function.
static curlioerr ioctlCallbackAsync(CURL *handle, int cmd, void *clientp)
{
    (void)handle;
    
    ASSERT(clientp);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(clientp);

    if (cmd == CURLIOCMD_RESTARTREAD)
        context->m_formDataStream.rewind(); /* clear counter to make the read callback restart */

    return CURLIOE_OK;
}

static int seekCallbackAsync(void* userp, curl_off_t offset, int origin)
{
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(userp);
    ASSERT(context);

    if (context->m_formDataStream.seek(offset, origin))
        return 0; // OK
    else {
        DEBUG_LOG("-> seekCallbackAsync() m_formDataStream.seek() failed\n");
        return 2; // seek error
    }
}

/*
  Functions registerd to curl handle which are called in filtering thread.

  - headerCallbackFiltering
  - writeCallbackFiltering

*/
static size_t headerCallbackFiltering(char* ptr, size_t size, size_t nmemb, void* data)
{
    ASSERT(data);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(data);
    if (context->m_httpState & HttpState_Cancelled)
        return 0;

    // This should never be called from multi_perform when deferred loading is activated.
    // Because this curl handle is paused.
    ASSERT(!(context->m_httpState & HttpState_DefersLoading));

    context->m_filteringRespHeaders.append(reinterpret_cast<const char*>(ptr), size * nmemb);

    return size * nmemb;
}

static size_t writeCallbackFiltering(char* ptr, size_t size, size_t nmemb, void* data)
{
    ASSERT(data);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(data);
    if (context->m_httpState & HttpState_Cancelled)
        return 0;

    // This should never be called from multi_perform when deferred loading is activated.
    // Because this curl handle is paused.
    ASSERT(!(context->m_httpState & HttpState_DefersLoading));

    context->m_filteringBody.append(reinterpret_cast<char*>(ptr), size * nmemb);

    return size * nmemb;
}

/*
  Functions that are called in main(webkit) thread via callOnMainThread function
  called in worker thread.

  - didRecvResponse
  - didRecvData
  - willSendRequest
  - didFinish
  - didFail
  - handleCancel
  - handleResponseHeaders
  - handleSetMimeType
  - handleDataUrl
  - notifyOriginFrame

*/
static void didRecvResponse(void* cmd)
{
    DEBUG_LOG("-> didRecvResponse()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    if (!d->m_cancelled) {
        if (command->m_data[1].typeCharPtr)
            d->m_response.setURL(WebCore::KURL(
                KURL(),
                command->m_data[1].typeCharPtr));

        if (command->m_data[2].typeCharPtr)
            d->m_response.setReplaceUrl(WebCore::KURL(
                KURL(),
                command->m_data[2].typeCharPtr));

        WebCore::ResourceHandleClient* client = d->client();
        if (client)
            client->didReceiveResponse(handle, d->m_response);

        d->m_response.setResponseFired(true);
    }

    if (command->m_data[1].typeCharPtr)
        delete [] command->m_data[1].typeCharPtr;

    if (command->m_data[2].typeCharPtr)
        delete [] command->m_data[2].typeCharPtr;

    if (command)
        delete command;

}

static void didRecvData(void* cmd)
{
    DEBUG_LOG("-> didRecvData()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    if (!d->m_cancelled) {
        if (!d->m_response.responseFired()) {
            d->m_response.setURL(KURL(KURL(), command->m_data[3].typeCharPtr));
            if (d->client())
                d->client()->didReceiveResponse(handle, d->m_response);
            d->m_response.setResponseFired(true);
            if (d->m_cancelled)
                goto cleancmd;
        }

        WebCore::ResourceHandleClient* client = d->client();
        if (client)
            client->didReceiveData(handle, 
                static_cast<char*>(command->m_data[1].typeCharPtr), 
                command->m_data[2].typeInt, 
                0);
    }

cleancmd:
    if (command->m_data[1].typeCharPtr)
        delete [] command->m_data[1].typeCharPtr;
    if (command->m_data[3].typeCharPtr)
        delete [] command->m_data[3].typeCharPtr;

    if (command)
        delete command;

}
static bool shouldRewriteRedirectToGET(int statusCode, const WTF::String& method)
{
    bool shouldRewrite = false;
    switch (statusCode) {
    case 301:
    case 302:
    case 303:
        if ("POST" == method)
            shouldRewrite = true;
        break;
    default:
        break;
    }
    return shouldRewrite;
}

#if OS(ORBIS)
static void willFilterSendRequest(void* cmd)
{
    DEBUG_LOG("-> willFilterSendRequest()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(command->m_data[1].typeVoidPtr);
    ASSERT(context);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    if (!d->m_cancelled) {
        WebCore::ResourceHandleClient* client = d->client();
        if (client) {
            WebCore::ResourceRequest filteredRequest = handle->firstRequest();
            filteredRequest.setRedirected(true);

            filteredRequest.setURL(context->m_KURL);
            filteredRequest.setHTTPMethod("GET"); // Any HTTP requests are replaced with "GET" forcely.
            filteredRequest.setHTTPBody(0);
            filteredRequest.clearHTTPContentType();
            filteredRequest.clearHTTPReferrer();

            d->m_firstRequest.setURL(context->m_KURL);
            d->m_response.setURL(context->m_KURL); // notify fake response to replace the block page URL.

            client->willSendRequest(handle, filteredRequest, d->m_response);

            filteredRequest.setRedirected(false);
        }
    }

    if (command)
        delete command;
}
#endif

static void willSendRequest(void* cmd)
{
    DEBUG_LOG("-> willSendRequest()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(command->m_data[1].typeVoidPtr);
    ASSERT(context);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    if (!d->m_cancelled) {
        WebCore::ResourceHandleClient* client = d->client();
        if (client) {

            WTF::String location = d->m_response.httpHeaderField("location");
            if (!location.isEmpty()) {
                WebCore::KURL newURL = context->m_KURL;
                WebCore::ResourceRequest redirectedRequest = handle->firstRequest();
                redirectedRequest.setURL(newURL);
                redirectedRequest.setRedirected(true);


                if (shouldRewriteRedirectToGET(d->m_response.httpStatusCode(), redirectedRequest.httpMethod())) {
                    redirectedRequest.setHTTPMethod("GET");
                    redirectedRequest.setHTTPBody(0);
                    redirectedRequest.clearHTTPContentType();
                    context->m_method = HttpMethod_Get;
                    context->m_httpState &= ~HttpState_IsPost;
                }
                if (client)
                    client->willSendRequest(handle, redirectedRequest, d->m_response);

                redirectedRequest.setRedirected(false);
                d->m_firstRequest.setURL(newURL);
            }
        }
    }

    if (command)
        delete command;
}

static void didFinish(void* cmd)
{
    DEBUG_LOG("-> didFinish()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    if (!d->m_cancelled) {
        if (!d->m_response.responseFired()) {

            d->m_response.setURL(KURL(KURL(), command->m_data[1].typeCharPtr));
            if (d->client())
                d->client()->didReceiveResponse(handle, d->m_response);
            d->m_response.setResponseFired(true);

        }

        if (d->client()) {
            d->client()->didFinishLoading(handle, 0);
            // At the timing of ResourceHandleManager::add(),
            // handle->ref() was called for ResourceHandle not to be deleted
            // while ResourceHandle is being treated as void* in worker thread.
            // So we must call handle->deref() to decrement the ref count
            // when resource loading is completed.
            handle->deref();
            handle = 0;
        }
    }

    if (command->m_data[1].typeCharPtr)
        delete [] command->m_data[1].typeCharPtr;
    if (command->m_data[2].typeVoidPtr) {
        ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(command->m_data[2].typeVoidPtr);
        if (context)
            delete context;
    }

    if (command)
        delete command;

#if ASYNC_REQUEST_DEBUG
    jobscount--;
    printf("didfinish() count = [%d]\n", jobscount);
#endif
}

static void didFail(void* cmd)
{
    DEBUG_LOG("-> didFail()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    if (!d->m_cancelled) {
        WebCore::ResourceHandleClient* client = d->client();
        if (client) {
            WebCore::ResourceError resourceError(
                String(),
                command->m_data[1].typeInt,
                WTF::String(command->m_data[2].typeCharPtr),
                WTF::String(command->m_data[3].typeCharPtr));
            resourceError.setIsTimeout(command->m_data[1].typeInt == CURLE_OPERATION_TIMEDOUT);
            client->didFail(handle, resourceError);
        }

        // At the timing of ResourceHandleManager::add(),
        // handle->ref() was called for ResourceHandle not to be deleted
        // while ResourceHandle is being treated as void* in worker thread.
        // So we must call handle->deref() to decrement the ref count
        // when resource loading is completed.
        handle->deref();
        handle = 0;
    }

    if (command->m_data[2].typeCharPtr)
        delete [] command->m_data[2].typeCharPtr;
    if (command->m_data[3].typeCharPtr)
        delete [] command->m_data[3].typeCharPtr;
    if (command->m_data[4].typeVoidPtr) {
        ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(command->m_data[4].typeVoidPtr);
        if (context)
            delete context;
    }

    if (command)
        delete command;

#if ASYNC_REQUEST_DEBUG
    jobscount--;
    printf("didfail() count = [%d]\n", jobscount);
#endif
}

static void handleCancel(void* cmd)
{
    DEBUG_LOG("-> handleCancel()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(command->m_data[1].typeVoidPtr);
    ASSERT(context);

    WebCore::ResourceHandleClient* client = handle->client();
    ASSERT(client);
    if (client) {
        WebCore::ResourceError error(String(),
            0,
            WTF::String(),
            WTF::String());
        error.setIsCancellation(true);
        client->didFail(handle, error);
    }
    handle->deref();
    handle = 0;

    if (context)
        delete context;
    
    if (command)
        delete command;

#if ASYNC_REQUEST_DEBUG
    jobscount--;
    printf("handleCancel() count = [%d]\n", jobscount);
#endif
}

static void handleResponseHeaders(void* cmd)
{
    DEBUG_LOG("-> handleResponseHeaders()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    if (!d->m_cancelled) {

        if (command->m_data[4].typeCharPtr) {
            char* top = command->m_data[4].typeCharPtr;
            char* headerCStr = strtok(top, "\r\n");
            while (headerCStr) {
                String header(static_cast<const char*>(headerCStr), (int)strlen(headerCStr));
                int splitPos = header.find(":");
                if (splitPos != -1)
                    d->m_response.setHTTPHeaderField(header.left(splitPos), header.substring(splitPos+1).stripWhiteSpace());
                headerCStr = strtok(0, "\r\n");
            }
        }

        d->m_response.setExpectedContentLength(command->m_data[1].typeLongLongInt);

        if (command->m_data[2].typeCharPtr)
            d->m_response.setURL(KURL(KURL(), command->m_data[2].typeCharPtr));

        d->m_response.setHTTPStatusCode(command->m_data[3].typeInt);

        d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField("Content-Type")));
        d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField("Content-Type")));
        d->m_response.setSuggestedFilename(filenameFromHTTPContentDisposition(d->m_response.httpHeaderField("Content-Disposition")));
    }

    if (command->m_data[2].typeCharPtr)
        delete [] command->m_data[2].typeCharPtr;
    if (command->m_data[4].typeCharPtr)
        delete [] command->m_data[4].typeCharPtr;

    if (command)
        delete command;

}

static void handleSetMimeType(void *cmd)
{
    DEBUG_LOG("-> handleSetMimeType()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(command->m_data[1].typeCharPtr));

    if (command->m_data[1].typeCharPtr)
        delete [] command->m_data[1].typeCharPtr;

    
    if (command)
        delete command;

}

static void handleDataUrl(void* cmd)
{
    DEBUG_LOG("-> handleDataUrl()\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(command->m_data[0].typeVoidPtr);
    ASSERT(handle);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    if (!d->m_cancelled)
        handleDataURL(handle);

    if (command)
        delete command;

    handle->deref();
}

static void notifyOriginFrame(void* cmd)
{
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(command->m_data[0].typeVoidPtr);
    ASSERT(context);
    WebCore::ResourceHandle* handle = reinterpret_cast<WebCore::ResourceHandle*>(context->job);
    ASSERT(handle);

    WebCore::ResourceHandleInternal* d = handle->getInternal();
    ASSERT(d);
    if (!d->m_cancelled) {
        if (context->m_isMainResource && context->m_chainForJSExt)
            d->m_response.setCert(context->m_chainForJSExt);
    }

    if (command)
        delete command;

}

void ResourceHandleManager::notifyBadCertSynchronously(void* cmd)
{
    DEBUG_LOG("-> notifyBadCertSynchronously\n");
    ResourceHandleCommand* command = reinterpret_cast<ResourceHandleCommand*>(cmd);
    ASSERT(command);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(command->m_data[0].typeVoidPtr);
    ASSERT(context);

    ResourceError error(command->m_data[1].typeInt, (const LChar*)(command->m_data[2].typeCharPtr));
    Vector<CString> pems;
    for (int i = 0; i < context->m_pemlen; i++)
        pems.insert(i, (const char*)(context->m_pems[i]));

    error.setCerts(pems);

    ASSERT(context->m_nwcontext);
    context->m_nwcontext->onCertificateVerificationRequest(error);
    context->m_confirm = error.confirmCert();
    DEBUG_LOG("<- notifyBadCertSynchronously\n");

    if (command->m_data[2].typeCharPtr)
        delete [] command->m_data[2].typeCharPtr;

    if (command)
        delete command;

    // signal to resume
    WTF::MutexLocker locker(*s_notifyBadCertConditionMutex);
    s_notifyBadCertCondition->signal();
}


/*
  Functions which are used from dispatchSynchronousJob().
  Classes in WebCore were not removed since it is safe.

  - handleLocalReceiveResponse
  - writeCallback
  - headerCallback
  - readCallback
 */
static void handleLocalReceiveResponse(CURL* handle, ResourceHandle* job, ResourceHandleInternal* d)
{
    char* url = 0;
    if (CURLE_OK != curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url))
        ASSERT(false);

    d->m_response.setURL(KURL(KURL(), url));
    if (d->client())
        d->client()->didReceiveResponse(job, d->m_response);
    d->m_response.setResponseFired(true);

}


// called with data after all headers have been processed via headerCallback
static size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* data)
{
    if (!data)
        return 0;

    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(data);
    ResourceHandle* job = context->job;
    ResourceHandleInternal* d = job->getInternal();
    
    if (d->m_cancelled)
        return 0;

    size_t totalSize = size * nmemb;
    long httpCode = 0;
    if (CURLE_OK == curl_easy_getinfo(d->m_handle, CURLINFO_RESPONSE_CODE, &httpCode)) {
        if ((300 <= httpCode && httpCode < 400)
            || (((httpCode == 401) || (httpCode == 407)) 
                && (context->m_httpState & (HttpState_HttpAuthInProcess | HttpState_ProxyAuthInProcess))
                && !(context->m_httpState & HttpState_AuthCancelled)))
            return totalSize;
    }

    if (!d->m_response.responseFired()) {
        handleLocalReceiveResponse(d->m_handle, job, d);
        if (d->m_cancelled)
            return 0;
    }

    if (d->client())
        d->client()->didReceiveData(job, static_cast<char*>(ptr), totalSize, 0);
    return totalSize;
}

size_t ResourceHandleManager::headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
{
    if (!data)
        return 0;

    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(data);
    ResourceHandle* job = context->job;
    ResourceHandleInternal* d = job->getInternal();

    if (d->m_cancelled)
        return 0;

    size_t totalSize = size * nmemb;
    ResourceHandleClient* client = d->client();

    String header(static_cast<const char*>(ptr), totalSize);

    extractAuthInfo(context, header, context->m_handle);

    if (header == "\r\n" || header == "\n") {
        long httpCode = 0;
        long connectCode = 0;
        curl_easy_getinfo(d->m_handle, CURLINFO_RESPONSE_CODE, &httpCode);
        curl_easy_getinfo(d->m_handle, CURLINFO_HTTP_CONNECTCODE, &connectCode);

        if (httpCode == 401 || httpCode == 407 || connectCode == 407)
            return prepareHandleAuthenticate(context, httpCode, connectCode, totalSize, data);

        // HTTP redirection
        if (300 <= httpCode && httpCode < 400) {
            KURL redirURL = KURL(context->m_KURL, d->m_response.httpHeaderField("location"));
            if (!redirURL.isEmpty()) {
                if (redirURL.protocolIsInHTTPFamily() && !redirURL.hasFragmentIdentifier() && context->m_KURL.hasFragmentIdentifier())
                    redirURL.setFragmentIdentifier(context->m_KURL.fragmentIdentifier());
                context->m_KURL = redirURL;
            
                ResourceRequest redirectedRequest = job->firstRequest();
                redirectedRequest.setURL(redirURL);

                if (shouldRewriteRedirectToGET(d->m_response.httpStatusCode(), redirectedRequest.httpMethod())) {
                    redirectedRequest.setHTTPMethod("GET");
                    redirectedRequest.setHTTPBody(0);
                    redirectedRequest.clearHTTPContentType();
                    context->m_method = HttpMethod_Get;
                    context->m_httpState &= ~HttpState_IsPost;
                }

                if (client)
                    client->willSendRequest(job, redirectedRequest, d->m_response);

                d->m_firstRequest.setURL(redirURL);

                return totalSize;
            }
        }

        double contentLength = 0;
        curl_easy_getinfo(d->m_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength);
        d->m_response.setExpectedContentLength(static_cast<long long int>(contentLength));

        d->m_response.setURL(context->m_KURL);
        d->m_response.setHTTPStatusCode(httpCode);
        d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField("Content-Type")));
        d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField("Content-Type")));
        d->m_response.setSuggestedFilename(filenameFromHTTPContentDisposition(d->m_response.httpHeaderField("Content-Disposition")));

        if (client)
            client->didReceiveResponse(job, d->m_response);
        d->m_response.setResponseFired(true);

    } else {
        int splitPos = header.find(':');
        if (splitPos != -1)
            d->m_response.setHTTPHeaderField(header.left(splitPos), header.substring(splitPos+1).stripWhiteSpace());
    }

    return totalSize;
}

// POST or PUT call this function to load data which should be uploaded to servers.
// Iterate through FormData elements and upload files.
// Carefully respect the given buffer size and fill the rest of the data at the next calls.
size_t readCallback(void* ptr, size_t size, size_t nmemb, void* data)
{
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(data);
    ASSERT(context);
    ResourceHandle* job = context->job;
    ASSERT(job);
    ResourceHandleInternal* d = job->getInternal();
    ASSERT(d);

    size_t sent = 0;
    if (d->m_cancelled
        || !size || !nmemb
        || !(d->m_formDataStream.hasMoreElements()?d->m_formDataStream.read(ptr, size, nmemb, sent):true))
        return CURL_READFUNC_ABORT;

    return sent;
}

// FIXME: ioctlCallback() and ioctlCallbackAsync() can be gathered into one function.
static curlioerr ioctlCallback(CURL *handle, int cmd, void *clientp)
{
    (void)handle;
    
    ASSERT(clientp);
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(clientp);
    ResourceHandle* job = context->job;
    ResourceHandleInternal* d = job->getInternal();

    if (cmd == CURLIOCMD_RESTARTREAD)
        d->m_formDataStream.rewind(); /* clear counter to make the read callback restart */

    return CURLIOE_OK;
}

static int seekCallback(void* userp, curl_off_t offset, int origin)
{
    ResourceHandleContext* context = reinterpret_cast<ResourceHandleContext*>(userp);
    ASSERT(context);
    ResourceHandle* job = context->job;
    ASSERT(job);
    ResourceHandleInternal* d = job->getInternal();
    ASSERT(d);

    if (d->m_formDataStream.seek(offset, origin))
        return 0; // OK
    else {
        DEBUG_LOG("-> seekCallback() m_formDataStream.seek() failed\n");
        return 2; // seek error
    }
}

size_t ResourceHandleManager::prepareHandleAuthenticate(ResourceHandleContext* context, long httpCode, long connectCode, size_t totalSize, void* data)
{
    //
    //  handle authentication
    //
    if (context->m_httpState & HttpState_AuthCancelled)
        return totalSize;

    if (httpCode == 401) {

        // comming 401 after 407 means that proxy auth was passed.
        if (context->m_httpState & HttpState_ProxyAuthInProcess)
            saveCredential(context, context->m_proxyChallenge, context->m_proxySpace, context->m_proxyKURL);

        // If there isn't a realm, we cancel authentication process.
        // By returning totalSize and ignoring authenticate handling,
        // libcurl can continue downloading HTTP 401's response body,
        // so postDidFinish will be called in success in the same way
        // as normal HTTP 200's response in processDownload().
        if (!(context->m_httpState & HttpState_HasRealmForHttpAuth))
            return totalSize;

        // if threre isn't a credential yet, we prepare to handle authenticate.
        if (!(context->m_httpState & HttpState_HasHttpCredential)
            && !(context->m_httpState & HttpState_HttpAuthInProcess)) {
            // This(returning zero) will cause CURLE_WRITE_ERROR.
            return 0;
        }
    }
    if (httpCode == 407 || connectCode == 407) {
        if (!(context->m_httpState & HttpState_ProxyUserPassWasSet)) {
            context->m_httpState |= HttpState_ProxyUserPassWasSet;
            if (context->m_httpState & HttpState_ProxyAuthInProcess)
                context->m_httpState &= ~HttpState_ProxyAuthInProcess;

            // libcurl tells 407 only once when user:pass is correct per one request.
            return 0;
        }

        context->m_httpState &= ~HttpState_ProxyUserPassWasSet;

        // if there isn't a realm, we cancel authentication process.
        if (!(context->m_httpState & HttpState_HasRealmForProxyAuth))
            return totalSize;

        if (!(context->m_httpState & HttpState_HasProxyCredential)
            && !(context->m_httpState & HttpState_ProxyAuthInProcess)) {
            // This(returning zero) causes CURLE_WRITE_ERROR.
            return 0;
        }
    }
    return totalSize;
}

/*
  Functions which are called when filtering is enabled.

  - convertToFilteringUrl
  - evaluateFilteringResult
*/
bool convertToFilteringUrl(ResourceHandleContext* context, bool& skipEval)
{
    char* convertedUrlBuf = 0;
    int convertedUrlLen = 0;
    const int defaultLen = 8192;
    convertedUrlBuf = reinterpret_cast<char*>(malloc(defaultLen));

    if (convertedUrlBuf)
        Manx::WebSecurity::convertURL(context->m_isMainResource, context->m_KURL.string().latin1().data(), convertedUrlBuf, defaultLen, &convertedUrlLen, skipEval);
    else
        return false;
    if (convertedUrlLen > defaultLen-1) {
        convertedUrlBuf = reinterpret_cast<char*>(realloc(reinterpret_cast<void*>(convertedUrlBuf), convertedUrlLen + 1));

        if (convertedUrlBuf)
            Manx::WebSecurity::convertURL(context->m_isMainResource, context->m_KURL.string().latin1().data(), convertedUrlBuf, convertedUrlLen + 1, &convertedUrlLen, skipEval);
        else
            return false;
    }
    String convertedUrlStr(convertedUrlBuf, convertedUrlLen);
    context->m_filteringurl = convertedUrlStr;
    free(convertedUrlBuf);
    return true;
}

/*
     if showPage == true, we will finish filtering process and prepare for requesting
     original url.
     if showPage == false, we will convert original url again

     then we will send request again.
 */
bool evaluateFilteringResult(ResourceHandleContext* context, bool& showPage)
{
    char* generatedUrlBuf = 0;
    int generatedUrlLen = 0;
    const int defaultLen = 8192;

    generatedUrlBuf = reinterpret_cast<char*>(malloc(defaultLen));
    if (generatedUrlBuf)
        Manx::WebSecurity::filterReturnURL(context->m_filteringRespHeaders.data(), context->m_filteringBody.data(), context->m_KURL.string().latin1().data(), generatedUrlBuf, defaultLen, &generatedUrlLen, showPage);
    else
        return false;

    if (showPage && generatedUrlLen > defaultLen-1) {
        generatedUrlBuf = reinterpret_cast<char*>(realloc(reinterpret_cast<void*>(generatedUrlBuf), generatedUrlLen + 1));
        if (generatedUrlBuf)
            Manx::WebSecurity::filterReturnURL(context->m_filteringRespHeaders.data(), context->m_filteringBody.data(), context->m_KURL.string().latin1().data(), generatedUrlBuf, generatedUrlLen+1, &generatedUrlLen, showPage);
        else
            return false;
    }

    String generatedUrlStr = String(generatedUrlBuf, generatedUrlLen);
    context->m_evaluatedkurl = KURL(KURL(), generatedUrlStr);
    if (generatedUrlBuf)
        free(generatedUrlBuf);

    return true;
}

/*
  - processFiltering (for filtering request)
  - processDownload (for normal request)
*/
bool ResourceHandleManager::processFiltering()
{
    /*
      1) look up filtering-queue if there are any contexts
         to handle filtering.
      2) verify url if it needs to be filtered using libfiltering API.
      3) if the url needs to be filtered, set flag to do curl_multi_perform 
         for filtering-multi-handle.
      4) if the url doesn't need to be filtered, context will be removed then inserted to
         worker-queue for normal requests.
      5) if the flag for executing curl_multi_perform is set,
         call curl_multi_perform to execute requests.
      6) before calling didFinish(), evaluate filtering respnse 
         to check if original request should be sent or blocking url should be sent.
    */


    // handle 1),2),3),4) here.
    startScheduledFilteringJobs();

    // handle 5) here.
    // call curl_multi_perform to process curl handles which are added in startScheduledFilteringJobs().

    int runningHandles = 0;
    while (curl_multi_perform(s_filteringMultiHandle, &runningHandles) == CURLM_CALL_MULTI_PERFORM) {
    }

    // check the curl messages indicating completed transfers
    // and free their resources
    while (true) {
        int messagesInQueue;
        CURLMsg* msg = curl_multi_info_read(s_filteringMultiHandle, &messagesInQueue);
        if (!msg) {
            if (runningHandles > messagesInQueue) {
                DEBUG_LOG("ResourceHandleManager::processFiltering() continue processing filtering\n");

                // this direct to look up filtering-queue again.
                return true;
            }
            break;
        }

        ASSERT(msg->easy_handle);
        ResourceHandleContext* context = 0;
        if (CURLE_OK != curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &context) && context)
            ASSERT(false);

        if (!context->job)
            continue;
        ASSERT(context->m_filteringhandle == msg->easy_handle);

        if (context->m_httpState & HttpState_Cancelled) {
            removeFromCurlForFiltering(context);
            // move context to worker-queue
            continue;
        }

        if (CURLMSG_DONE != msg->msg)
            continue;

        if (CURLE_OK == msg->data.result) {

            if (context->m_httpState & HttpState_Cancelled) {
                removeFromCurlForFiltering(context);
                // move context to worker-queue
                continue;
            }

            // handle 6) here.
            // before didFinish, 
            // I should evaluate a response returned from a rating server using libwebfiltering.
            String generatedUrl;
            bool showPage = false;
            evaluateFilteringResult(context, showPage);
            if (showPage) {
                // move ResourceHandleContext from filtering-queue to worker-queue.
                if (context->m_KURL != context->m_evaluatedkurl) {
                    context->m_KURL = context->m_evaluatedkurl;
#if OS(ORBIS)
                    postWillFilterSendRequest(context);
#endif
                }

                WTF::MutexLocker workerMutexlock(*s_workerThreadMutex);
                m_resourceHandleContextList.append(context);

#if OS(ORBIS)
                WTF::MutexLocker workerMultiMutexlock(*s_workerThreadMultiMutex);
                wakeupSelect(s_WPIPE[PUMP_WORKERTHREAD]);
#else
                WTF::MutexLocker workerConditionlock(*s_workerThreadConditionMutex);
                s_workerThreadCondition->signal();
#endif
            } else {
                bool skipEval = false;
                convertToFilteringUrl(context, skipEval);
                if (skipEval) {
                    WTF::MutexLocker workerMutexlock(*s_workerThreadMutex);
                    m_resourceHandleContextList.append(context);
#if OS(ORBIS)
                    WTF::MutexLocker workerMultiMutexlock(*s_workerThreadMultiMutex);
                    wakeupSelect(s_WPIPE[PUMP_WORKERTHREAD]);
#else
                    WTF::MutexLocker workerConditionlock(*s_workerThreadConditionMutex);
                    s_workerThreadCondition->signal();
#endif
                } else {
                    removeFromCurlForFiltering(context);
                    context->m_filteringRespHeaders.clear();
                    context->m_filteringBody.clear();
                    startFilteringJob(context);
                    return true;
                }
            }

        } else {
            WTF::MutexLocker workerMutexlock(*s_workerThreadMutex);
            m_resourceHandleContextList.append(context);
#if OS(ORBIS)
            WTF::MutexLocker workerMultiMutexlock(*s_workerThreadMultiMutex);
            wakeupSelect(s_WPIPE[PUMP_WORKERTHREAD]);
#else
            WTF::MutexLocker workerConditionlock(*s_workerThreadConditionMutex);
            s_workerThreadCondition->signal();
#endif

        }
        removeFromCurlForFiltering(context);
    }

    return startScheduledFilteringJobs();
}

void ResourceHandleManager::saveCredential(ResourceHandleContext* context, AuthenticationChallenge& challenge, ProtectionSpace& space, KURL& kurl)
{
    if (!challenge.isNull()) {
        Credential inputCredential(challenge.m_user, challenge.m_pass, CredentialPersistencePermanent);
        CredentialTransformData data = CredentialTransformData(kurl, space, inputCredential);

        Credential newSavedCredential = CredentialBackingStore::instance()->getLogin(data.url());
        if (newSavedCredential != data.credential()) {
            if (newSavedCredential.isEmpty())
                CredentialBackingStore::instance()->addLogin(kurl, data.protectionSpace(), data.credential());
            else
                CredentialBackingStore::instance()->updateLogin(kurl, data.protectionSpace(), data.credential());
        }
    }
}

void ResourceHandleManager::saveAuthCredential(ResourceHandleContext* context)
{
    if (context->m_httpState & (HttpState_HttpAuthInProcess | HttpState_ProxyAuthInProcess)) {

        if (context->m_httpState & HttpState_HttpAuthModeFromHeader)
            saveCredential(context, context->m_httpChallenge, context->m_httpSpace, context->m_KURL);

        if (context->m_httpState & HttpState_ProxyAuthModeFromHeader)
            saveCredential(context, context->m_proxyChallenge, context->m_proxySpace, context->m_proxyKURL);

        context->m_httpState &= ~(HttpState_HttpAuthInProcess | HttpState_ProxyAuthInProcess | HttpState_HttpAuthModeFromHeader | HttpState_ProxyAuthModeFromHeader);
        context->m_httpAuthType = context->m_proxyAuthType = Manx::HttpAuthNone;
    }
}

bool ResourceHandleManager::processDownload()
{
    int runningHandles = 0;
    while (curl_multi_perform(s_workerMultiHandle, &runningHandles) == CURLM_CALL_MULTI_PERFORM) {
    }

    // check the curl messages indicating completed transfers
    // and free their resources
    while (true) {
        int messagesInQueue;
        CURLMsg* msg = curl_multi_info_read(s_workerMultiHandle, &messagesInQueue);
        if (!msg) {
            if (runningHandles > 0)
                return true;
            return false;
        }

        // find the node which has same d->m_handle as completed transfer
        ASSERT(msg->easy_handle);
        ResourceHandleContext* context = 0;
        if (CURLE_OK != curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &context) && context)
            ASSERT(false);

        ASSERT(context->m_handle == msg->easy_handle);

        if (context->m_httpState & HttpState_Cancelled) {
            removeFromCurl(context);
            continue;
        }

        if (CURLMSG_DONE != msg->msg)
            continue;

        if (CURLE_WRITE_ERROR == msg->data.result) {
            long httpCode = 0;
            long connectCode = 0;
            curl_easy_getinfo(context->m_handle, CURLINFO_RESPONSE_CODE, &httpCode);
            curl_easy_getinfo(context->m_handle, CURLINFO_HTTP_CONNECTCODE, &connectCode);

            //
            //  URL is blocked by filtering while redirection.
            //
            if (context->m_filteringActivatedStatus) {
                if (context->m_blockedLocation) {
                    context->m_blockedLocation = false;

                    {   WTF::MutexLocker lock(*s_workerThreadMultiMutex);
                        curl_multi_remove_handle(s_workerMultiHandle, context->m_handle);
                        curl_easy_cleanup(context->m_handle);
                
                        context->m_KURL = context->m_evaluatedkurl;
                        postWillSendRequest(context);
                        initializeHandleForAsync(context);
                        curl_multi_add_handle(s_workerMultiHandle, context->m_handle);
                    }

                    while (curl_multi_perform(s_workerMultiHandle, &runningHandles) == CURLM_CALL_MULTI_PERFORM) {
                    }
                    continue;
                }
            }
            //
            //  handle authentication
            //
            if ((httpCode == 401 
                && (context->m_httpState & HttpState_HasRealmForHttpAuth) 
                && !(context->m_httpState & HttpState_HttpAuthInProcess)) 
                || ((connectCode == 407 || httpCode == 407) 
                    && (context->m_httpState & HttpState_HasRealmForProxyAuth)
                    && !(context->m_httpState & HttpState_ProxyAuthInProcess))) {

                // delegate the process to worker thread for authentication.
                callOnMainThread(handleAuthentication, context);

#if OS(ORBIS)
                curl_multi_remove_handle(s_workerMultiHandle, context->m_handle);
#endif
                setAuthType(context);
                curl_easy_reset(context->m_handle);
                initializeHandleForAsync(context);
                continue;
            }
        }

        //
        //  handle bad certificate
        //
        if (CURLE_SSL_CACERT == msg->data.result || CURLE_PEER_FAILED_VERIFICATION == msg->data.result) {
            if (!context->m_chainForBadCertNotify) {
                {   WTF::MutexLocker lock(*s_workerThreadMultiMutex);
                    curl_multi_remove_handle(s_workerMultiHandle, context->m_handle);
                }

                curl_easy_cleanup(context->m_handle);

                ResourceHandleManager::sharedInstance()->initializeHandleForAsync(context);
                curl_easy_setopt(context->m_handle, CURLOPT_SSL_VERIFYHOST, false);
                curl_easy_setopt(context->m_handle, CURLOPT_SSL_VERIFYPEER, false);

                {   WTF::MutexLocker lock(*s_workerThreadMultiMutex);
                    curl_multi_add_handle(s_workerMultiHandle, context->m_handle);
                }
                while (curl_multi_perform(s_workerMultiHandle, &runningHandles) == CURLM_CALL_MULTI_PERFORM) { }
            } else {
                removeFromCurl(context);
                context->m_curlResult = msg->data.result;

                /*
                  Once Application(User) confirmed the bad certificate,
                  browser automatically cofirms the domain.
                */
                handleSslBadCertificate(context);
            }

            continue;
            
        }

        if (CURLE_OK == msg->data.result) {

            long httpCode;
            curl_easy_getinfo(context->m_handle, CURLINFO_RESPONSE_CODE, &httpCode);

            if (httpCode != 401 && httpCode != 407)
                saveAuthCredential(context);
            else {
                if (!(context->m_httpState & HttpState_AuthCancelled) && ((httpCode == 401 && (context->m_httpState & HttpState_HasRealmForHttpAuth)) || (httpCode == 407 && (context->m_httpState & HttpState_HasRealmForProxyAuth)))) {
                    // delegate the process to worker thread for authentication.
                    callOnMainThread(handleAuthentication, context);
                    
#if OS(ORBIS)
                    curl_multi_remove_handle(s_workerMultiHandle, context->m_handle);
#endif
                    setAuthType(context);
                    curl_easy_reset(context->m_handle);
                    initializeHandleForAsync(context);
                    continue;
                }
            }

            if (context->m_httpState & HttpState_Cancelled) {
                removeFromCurl(context);
                continue;
            }

            removeFromCurl(context);
            postDidFinish(context);

        } else {
            char* url = 0;
            if (CURLE_OK == curl_easy_getinfo(context->m_handle, CURLINFO_EFFECTIVE_URL, &url) && url) {
                if (context->m_rawUrl)
                    fastFree(context->m_rawUrl);

                context->m_rawUrl = fastStrDup(url);
            }
            context->m_curlResult = msg->data.result;
#ifndef NDEBUG
            fprintf(stderr, "Curl ERROR for url='%s', error: '%s'\n", url, curl_easy_strerror(msg->data.result));
#endif

            long httpCode = 0;
            long connectCode = 0;
            curl_easy_getinfo(context->m_handle, CURLINFO_RESPONSE_CODE, &httpCode);
            curl_easy_getinfo(context->m_handle, CURLINFO_HTTP_CONNECTCODE, &connectCode);

            // Redirection Check
            if ((httpCode == 305) || (httpCode == 306)) {
                removeFromCurl(context);
                postDidFinish(context);
                continue;
            }

            if (Manx::WebSecurity::isBlockedPageOnFilterProxy(httpCode, connectCode, msg->data.result)) {
                {   WTF::MutexLocker lock(*s_workerThreadMultiMutex);
                    curl_multi_remove_handle(s_workerMultiHandle, context->m_handle);
                    curl_easy_cleanup(context->m_handle);
                        
                    WTF::String url = Manx::WebSecurity::getBlockedPageOnFilterProxyURL();
                    context->m_KURL = KURL(context->m_KURL, url);
                    postWillSendRequest(context);
                    initializeHandleForAsync(context);
                    curl_multi_add_handle(s_workerMultiHandle, context->m_handle);
                }

                while (curl_multi_perform(s_workerMultiHandle, &runningHandles) == CURLM_CALL_MULTI_PERFORM) {
                }
                continue;
            } 

            // If CURLE_RECV_ERROR occurred on HTTPS, this request would be resetted from the server.
            // In this condition, one more resource request will be created with CURLOPT_FRESH_CONNECT.
            // The option specifies to create new connection to the server, and it doesn't re-use
            // connections pooled in cURL.
            if ((CURLE_RECV_ERROR == msg->data.result) && (context->m_KURL.protocolIs("https")) && (context->m_curlRecvErrorRetryCount < 5)) {
                context->m_curlRecvErrorRetryCount++;

                s_workerThreadMultiMutex->lock();
                curl_multi_remove_handle(s_workerMultiHandle, context->m_handle);
                curl_easy_cleanup(context->m_handle);

                initializeHandleForAsync(context);
                curl_easy_setopt(context->m_handle, CURLOPT_FRESH_CONNECT, 1);
                curl_multi_add_handle(s_workerMultiHandle, context->m_handle);
                s_workerThreadMultiMutex->unlock();

                while (curl_multi_perform(s_workerMultiHandle, &runningHandles) == CURLM_CALL_MULTI_PERFORM) {
                }
                continue;
            }

            removeFromCurl(context);
            postDidFail(context);

        }
    }
}

void ResourceHandleManager::removeFromCurl(ResourceHandleContext* context)
{
    ASSERT(context->m_handle);
    if (!context->m_handle)
        return;
    {   WTF::MutexLocker lock(*s_workerThreadMultiMutex);
        curl_multi_remove_handle(s_workerMultiHandle, context->m_handle);
    }
    curl_easy_cleanup(context->m_handle);

    context->m_handle = 0;

}

void ResourceHandleManager::removeFromCurlForFiltering(ResourceHandleContext* context)
{
    ASSERT(context->m_filteringhandle);
    if (!context->m_filteringhandle)
        return;
    {   WTF::MutexLocker lock(*s_workerThreadMultiMutex);
        curl_multi_remove_handle(s_filteringMultiHandle, context->m_filteringhandle);
    }
    curl_easy_cleanup(context->m_filteringhandle);

    context->m_filteringhandle = 0;

}

void ResourceHandleManager::setupPUT(ResourceHandleContext* context, struct curl_slist** headers)
{
    ASSERT(context);

    curl_easy_setopt(context->m_handle, CURLOPT_IOCTLFUNCTION, ioctlCallback);
    curl_easy_setopt(context->m_handle, CURLOPT_IOCTLDATA, reinterpret_cast<void*>(context));

    curl_easy_setopt(context->m_handle, CURLOPT_PUT, TRUE);

    curl_easy_setopt(context->m_handle, CURLOPT_READFUNCTION, readCallback);
    curl_easy_setopt(context->m_handle, CURLOPT_READDATA, context);
/*
    // FIXME: Following implementation suggests that form data included in "httpbody"
    //        can be involved in FormData object. By using this way, PUT and PUT(Async)
    //        can use same upload process. (readCallback is not needed.)
    Vector<FormDataElement> elements;
    int elements = 0;

    if (!context->m_formData) {
        RefPtr<FormData> syncRequestFormData = FormData::create();
        ResourceHandleInternal* d = context->job->getInternal();
        context->job->firstRequest().httpBody()->flatten(d->m_postBytes);
        syncRequestFormData->appendData(d->m_postBytes.data(), d->m_postBytes.size());

        context->m_formData = syncRequestFormData;
        elements = syncRequestFormData->elements();
    }
*/
}    

void ResourceHandleManager::setupPUTAsync(ResourceHandleContext* context, struct curl_slist** headers)
{
    ASSERT(context);

    curl_easy_setopt(context->m_handle, CURLOPT_IOCTLFUNCTION, ioctlCallbackAsync);
    curl_easy_setopt(context->m_handle, CURLOPT_IOCTLDATA, reinterpret_cast<void*>(context));

    if ((!context->m_formData) || (!context->m_formData->elements().size()))
        return;

    curl_easy_setopt(context->m_handle, CURLOPT_PUT, TRUE);

    curl_easy_setopt(context->m_handle, CURLOPT_READFUNCTION, readCallbackAsync);
    curl_easy_setopt(context->m_handle, CURLOPT_READDATA, context);
}

/* Calculate the length of the POST.
   Force chunked data transfer if size of files can't be obtained.
 */
void ResourceHandleManager::setupPOST(ResourceHandle* job, struct curl_slist** headers)
{
    ResourceHandleInternal* d = job->getInternal();
    // CURL_REDIR_POST_302 and CURL_REDIR_POST_303 don't change the HTTP method,
    // so if the first request's method were POST and then redirected, the second request's
    // method would be set to POST when using CURL_REDIR_POST_302 or CURL_REDIR_POST_303.
    // Although the behavior of CURL_REDIR_POST_302 is conformant to RFC,
    // defact standard's behavior is different and changes to GET method, 
    // and the behavior of CURL_REDIR_POST_303 isn't conformant to RFC.
    curl_easy_setopt(d->m_handle, CURLOPT_POST, TRUE);
    curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, 0);

    if (!job->firstRequest().httpBody())
        return;

    Vector<FormDataElement> elements = job->firstRequest().httpBody()->elements();
    size_t numElements = elements.size();
    if (!numElements)
        return;

    // Do not stream for simple POST data
    if (numElements == 1) {
        job->firstRequest().httpBody()->flatten(d->m_postBytes);
        if (d->m_postBytes.size()) {
            curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, d->m_postBytes.size());
            curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDS, d->m_postBytes.data());
        }
        return;
    }

    // Obtain the total size of the POST
    // The size of a curl_off_t could be different in WebKit and in cURL depending on
    // compilation flags of both. For CURLOPT_POSTFIELDSIZE_LARGE we have to pass the
    // right size or random data will be used as the size.
    static int expectedSizeOfCurlOffT = 0;
    if (!expectedSizeOfCurlOffT) {
        curl_version_info_data* infoData = curl_version_info(CURLVERSION_NOW);
        if (infoData->features & CURL_VERSION_LARGEFILE)
            expectedSizeOfCurlOffT = sizeof(long long);
        else
            expectedSizeOfCurlOffT = sizeof(int);
    }

    static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT * 8 - 1)) - 1;
    curl_off_t size = 0;
    bool chunkedTransfer = false;
    for (size_t i = 0; i < numElements; i++) {
        FormDataElement element = elements[i];
        if (element.m_type == FormDataElement::encodedFile) {
            long long fileSizeResult;
            if (getFileSize(element.m_filename, fileSizeResult)) {
                if (fileSizeResult > maxCurlOffT) {
                    // File size is too big for specifying it to cURL
                    chunkedTransfer = true;
                    break;
                }
                size += fileSizeResult;
            } else {
                chunkedTransfer = true;
                break;
            }
        } else
            size += elements[i].m_data.size();
    }

    // cURL guesses that we want chunked encoding as long as we specify the header
    if (chunkedTransfer)
        *headers = curl_slist_append(*headers, "Transfer-Encoding: chunked");
    else {
        if (sizeof(long long) == expectedSizeOfCurlOffT)
            curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE_LARGE, (long long)size);
        else
            curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE_LARGE, (int)size);
    }

    curl_easy_setopt(d->m_handle, CURLOPT_READFUNCTION, readCallback);
    curl_easy_setopt(d->m_handle, CURLOPT_READDATA, job);
}

void ResourceHandleManager::setupPOSTAsync(ResourceHandleContext* context, struct curl_slist** headers)
{
    ASSERT(context);

    // CURL_REDIR_POST_302 and CURL_REDIR_POST_303 don't change the HTTP method,
    // so if the first request's method were POST and then redirected, the second request's
    // method would be set to POST when using CURL_REDIR_POST_302 or CURL_REDIR_POST_303.
    // Although the behavior of CURL_REDIR_POST_302 is conformant to RFC,
    // defact standard's behavior is different and changes to GET method, 
    // and the behavior of CURL_REDIR_POST_303 isn't conformant to RFC.
    curl_easy_setopt(context->m_handle, CURLOPT_POST, TRUE);
    curl_easy_setopt(context->m_handle, CURLOPT_POSTFIELDSIZE, 0);

    if (!context->m_formData)
        return;

    Vector<FormDataElement> elements = context->m_formData->elements();
    size_t numElements = elements.size();
    if (!numElements)
        return;

    // Do not stream for simple POST data
    if (numElements == 1) {
        context->m_formData->flatten(context->m_postBytes);
        if (context->m_postBytes.size()) {
            curl_easy_setopt(context->m_handle, CURLOPT_POSTFIELDSIZE, context->m_postBytes.size());
            curl_easy_setopt(context->m_handle, CURLOPT_POSTFIELDS, context->m_postBytes.data());
        }
        return;
    }

    // Obtain the total size of the POST
    // The size of a curl_off_t could be different in WebKit and in cURL depending on
    // compilation flags of both. For CURLOPT_POSTFIELDSIZE_LARGE we have to pass the
    // right size or random data will be used as the size.
    static int expectedSizeOfCurlOffT = 0;
    if (!expectedSizeOfCurlOffT) {
        curl_version_info_data* infoData = curl_version_info(CURLVERSION_NOW);
        if (infoData->features & CURL_VERSION_LARGEFILE)
            expectedSizeOfCurlOffT = sizeof(long long);
        else
            expectedSizeOfCurlOffT = sizeof(int);
    }

    static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT * 8 - 1)) - 1;
    curl_off_t size = 0;
    bool chunkedTransfer = false;
    for (size_t i = 0; i < numElements; i++) {
        FormDataElement element = elements[i];
        if (element.m_type == FormDataElement::encodedFile) {
            long long fileSizeResult;
            if (getFileSize(element.m_filename, fileSizeResult)) {
                if (fileSizeResult > maxCurlOffT) {
                    // File size is too big for specifying it to cURL
                    chunkedTransfer = true;
                    break;
                }
                size += fileSizeResult;
            } else {
                chunkedTransfer = true;
                break;
            }
        } else
            size += elements[i].m_data.size();
    }

    // cURL guesses that we want chunked encoding as long as we specify the header
    if (chunkedTransfer)
        *headers = curl_slist_append(*headers, "Transfer-Encoding: chunked");
    else {
        if (sizeof(long long) == expectedSizeOfCurlOffT) {
            curl_easy_setopt(context->m_handle, CURLOPT_POSTFIELDSIZE_LARGE, (long long)size);
            DEBUG_LOG("set CURLOPT_POSTFIELDSIZE_LARGE: (long)size=0x%llx\n", (long long)size);
        } else {
            curl_easy_setopt(context->m_handle, CURLOPT_POSTFIELDSIZE_LARGE, (int)size);
            DEBUG_LOG("set CURLOPT_POSTFIELDSIZE_LARGE: (int)size=0x%llx\n", (long long)size);
        }
    }

    curl_easy_setopt(context->m_handle, CURLOPT_READFUNCTION, readCallbackAsync);
    curl_easy_setopt(context->m_handle, CURLOPT_READDATA, context);
}

void ResourceHandleManager::add(ResourceHandle* job, NetworkingContext* nwcontext)
{
    // We call handle->ref() for ResourceHandle not to be deleted
    // while ResourceHandle is being treated as void* in worker thread.
    // So we must call handle->deref() to decrement the ref count
    // when resource loading is completed.
    job->ref();

#if ASYNC_REQUEST_DEBUG
    jobscount++;
    printf("ResourceHandleManager::add() count = [%d]\n", jobscount);
#endif

    ResourceHandleContext* context = new ResourceHandleContext(job);
    ASSERT(context);

    context->m_KURL = job->firstRequest().url().copy();
    context->m_postBytes = job->getInternal()->m_postBytes;
    context->m_formDataStream = job->getInternal()->m_formDataStream;
    context->m_httpState |= context->m_KURL.protocolIs("https")?HttpState_IsHttps:0;
    context->m_filteringActivatedStatus = Manx::WebSecurity::activatedStatus();
    context->m_host = fastStrDup(context->m_KURL.host().latin1().data());
    context->m_nwcontext = nwcontext;
    if (job->firstRequest().httpBody())
        context->m_formData = job->firstRequest().httpBody()->deepCopy();

    const HTTPHeaderMap headerFields =  job->getInternal()->m_firstRequest.httpHeaderFields(); 
    setCustomHeaders(headerFields, context);

    ResourceHandleInternal* handle = job->getInternal();
    if (handle) {
        context->m_isMainResource = handle->m_firstRequest.isMainResource();
        handle->m_response.setIsMainResource(context->m_isMainResource);
    }
    
    DEBUG_LOG("ResourceHandleManager::add url = [%s]\n", context->m_KURL.string().latin1().data());

    if (context->m_filteringActivatedStatus
        && !context->m_KURL.protocolIsData()
        && !context->m_KURL.isLocalFile()
        && !(context->m_KURL.host() == "psvitareg.trendmicro.com")) {
        checkAndCreateFilteringThread();

        {   WTF::MutexLocker lock(*s_filteringThreadMutex);
            m_filteringResourceHandleContextList.append(context);
        }
#if OS(ORBIS)
        WTF::MutexLocker condLock(*s_filteringThreadConditionMutex);
        s_filteringThreadCondition->signal();
        DEBUG_LOG("<- s_filteringThreadCondition->signal() time = [%f]\n", currentTime());
#endif
    } else {
        {   WTF::MutexLocker lock(*s_workerThreadMutex);
            m_resourceHandleContextList.append(context);
        }

#if OS(ORBIS)
        WTF::MutexLocker workerMultiMutexlock(*s_workerThreadMultiMutex);
        s_jobToContextMap.add(context->job, context);
        wakeupSelect(s_WPIPE[PUMP_WORKERTHREAD]);
#else
        WTF::MutexLocker condLock(*s_workerThreadConditionMutex);
        s_workerThreadCondition->signal();
        DEBUG_LOG("<- s_workerThreadCondition->signal() time = [%f]\n", currentTime());
#endif
    }
}

bool ResourceHandleManager::removeScheduledJob(ResourceHandle* job)
{
    {   WTF::MutexLocker lock(*s_workerThreadMutex);
        int size = m_resourceHandleContextList.size();

        for (int i = 0; i < size; i++) {
            if (job == m_resourceHandleContextList[i]->job) {
                m_resourceHandleContextList.remove(i);
                job->deref();
                return true;
            }
        }
    }
    return false;
}

bool ResourceHandleManager::startScheduledFilteringJobs()
{
    bool started = false;
    bool workerStart = false;

    WTF::MutexLocker lock(*s_filteringThreadMutex);

    while (!m_filteringResourceHandleContextList.isEmpty()) {
        ResourceHandleContext* context = m_filteringResourceHandleContextList[0];
        m_filteringResourceHandleContextList.remove(0);

        DEBUG_LOG("ResourceHandleManager::startScheduledFilteringJobs url = [%s]\n", context->m_KURL.string().latin1().data());

        bool skipEval = false;
        convertToFilteringUrl(context, skipEval);
        if (skipEval) {
            WTF::MutexLocker lock(*s_workerThreadMutex);
            m_resourceHandleContextList.append(context);

            workerStart = true;

        } else {
            // add curl handle to multi interface for filtering url.
            startFilteringJob(context);
            started = true;
        }
    }

    if (workerStart) {
        // request original url
        WTF::MutexLocker lock(*s_workerThreadConditionMutex);
        s_workerThreadCondition->signal();
#if OS(ORBIS)
        WTF::MutexLocker workerMutexlock(*s_workerThreadMultiMutex);
        wakeupSelect(s_WPIPE[PUMP_WORKERTHREAD]);
#endif
    }

    return started;
}

bool ResourceHandleManager::startScheduledJobs()
{
    bool started = false;

    {   WTF::MutexLocker lock(*s_workerThreadMutex);

        while (!m_resourceHandleContextList.isEmpty()) {
            ResourceHandleContext* context = m_resourceHandleContextList[0];
            m_resourceHandleContextList.remove(0);

            DEBUG_LOG("ResourceHandleManager::startScheduledJobs url = [%s]\n", context->m_KURL.string().latin1().data());
            startJob(context);
            started = true;
        }
    }

    return started;
}

void ResourceHandleManager::dispatchSynchronousJob(ResourceHandle* job, NetworkingContext* nwcontext)
{

#if ASYNC_REQUEST_DEBUG
    jobscount++;
    printf("ResourceHandleManager::dispatchSynchronousJob() count = [%d]\n", jobscount);
#endif

    if (job->firstRequest().url().protocolIsData()) {
        handleDataURL(job);
        return;
    }

    ResourceHandleInternal* handle = job->getInternal();
    ResourceHandleContext* context = new ResourceHandleContext(job);
    ASSERT(context);

    context->m_syncReq = true;
    context->m_KURL = job->firstRequest().url();
    context->m_postBytes = job->getInternal()->m_postBytes;
    context->m_formDataStream = job->getInternal()->m_formDataStream;
    context->m_httpState |= context->m_KURL.protocolIs("https")?HttpState_IsHttps:0;
    context->m_filteringActivatedStatus = Manx::WebSecurity::activatedStatus();
    context->m_host = fastStrDup(context->m_KURL.host().latin1().data());
    context->m_nwcontext = nwcontext;

    DEBUG_LOG("ResourceHandleManager::dispatchSynchronousJob url = [%s]\n", context->m_KURL.string().latin1().data());
    initializeHandleForSync(context);

    // curl_easy_perform blocks until the transfer is finished.
    CURLcode ret =  curl_easy_perform(handle->m_handle);

    long httpCode = 0;
    curl_easy_getinfo(handle->m_handle, CURLINFO_RESPONSE_CODE, &httpCode);

    if (ret == CURLE_WRITE_ERROR && (httpCode == 401 || httpCode == 407)) {
        setAuthType(context);
#ifdef ENABLE_PERSISTENT_CONNECTION_FOR_SYNCHRONOUS_COMMUNICATION
        if (context->m_handle == curlEasyHandleForSync()) {
            curl_easy_reset(context->m_handle);
            context->m_handle = 0;
            if (context->m_rawUrl) {
                fastFree(context->m_rawUrl); 
                context->m_rawUrl = 0;
            }
        }
#endif
        handleAuthentication(context);
        ret = curl_easy_perform(context->m_handle);
        if (ret) {
            ResourceError error(String(handle->m_url), ret, String(handle->m_url), String(curl_easy_strerror(ret)));
            job->client()->didFail(job, error);
        } else
            handle->client()->didFinishLoading(job, 0);

#ifdef ENABLE_PERSISTENT_CONNECTION_FOR_SYNCHRONOUS_COMMUNICATION
        curl_easy_reset(context->m_handle);
#else
        curl_easy_cleanup(context->m_handle);
#endif
        fastFree(context->m_rawUrl); 
        context->m_rawUrl = 0;
    } else {
        if (ret) {
            ResourceError error(String(handle->m_url), ret, String(handle->m_url), String(curl_easy_strerror(ret)));
            handle->client()->didFail(job, error);
        }

#ifdef ENABLE_PERSISTENT_CONNECTION_FOR_SYNCHRONOUS_COMMUNICATION
        curl_easy_reset(context->m_handle);
#else
        curl_easy_cleanup(handle->m_handle);
#endif
    }

    delete context;

#if ASYNC_REQUEST_DEBUG
    jobscount--;
    printf("ResourceHandleManager::dispatchSynchronousJob() count = [%d]\n", jobscount);
#endif
}

void ResourceHandleManager::startJob(ResourceHandleContext* context)
{
    ASSERT(context);

    if (context->m_KURL.protocolIsData()) {
        ResourceHandleCommand* cmd = new ResourceHandleCommand();
        ASSERT(cmd);
        cmd->m_data[0].typeVoidPtr = context->job;
        callOnMainThread(handleDataUrl, cmd);
        return;
    }

    initializeHandleForAsync(context);

    CURLMcode ret = CURLM_OK;
    {   WTF::MutexLocker lock(*s_workerThreadMultiMutex);
        ret = curl_multi_add_handle(s_workerMultiHandle, context->m_handle);
    }
    if (ret) {
#ifndef NDEBUG
        fprintf(stderr, "Error %d starting job %s\n", ret, encodeWithURLEscapeSequences(context->m_KURL.string()).latin1().data());
#endif
        postHandleCancel(context);
    }
}

void ResourceHandleManager::startFilteringJob(ResourceHandleContext* context)
{
    ASSERT(context);

    initializeHandleForFiltering(context);

    CURLMcode ret = CURLM_OK;
    {   WTF::MutexLocker lock(*s_workerThreadMultiMutex);
        ret = curl_multi_add_handle(s_filteringMultiHandle, context->m_filteringhandle);
    }
    if (ret) {
        // This is a critical situation 
        // since there are no reason to fail curl_multi_add_handle.
        ASSERT(false);
        return;
    }
}

void ResourceHandleManager::setCustomHeaders(const HTTPHeaderMap& headerFields, ResourceHandleContext* context)
{
    if (headerFields.size() > 0) {
        HTTPHeaderMap::const_iterator end = headerFields.end();
        for (HTTPHeaderMap::const_iterator it = headerFields.begin(); it != end; ++it) {
            String key = it->key;
            String value = it->value;
            String headerString(key);
            headerString.append(": ");
            headerString.append(value);
            CString headerLatin1 = headerString.latin1();
            context->m_customHeaders = curl_slist_append(context->m_customHeaders, headerLatin1.data());
            if (context->m_filteringActivatedStatus) {
                if ((key == "User-Agent") && (context->m_filteringActivatedStatus))
                    continue;
                context->m_filteringHeadersSlist = curl_slist_append(context->m_filteringHeadersSlist, headerLatin1.data());
            }
        }
        CString expectString("Expect:");
        context->m_customHeaders = curl_slist_append(context->m_customHeaders, expectString.data());
        if (context->m_filteringActivatedStatus)
            context->m_filteringHeadersSlist = curl_slist_append(context->m_filteringHeadersSlist, expectString.data());

        String acceptLanguageString("Accept-Language: ");
        acceptLanguageString.append(Manx::Locale::getLanguage());
        context->m_customHeaders = curl_slist_append(context->m_customHeaders, acceptLanguageString.latin1().data());
        if (context->m_filteringActivatedStatus)
            context->m_customHeaders = curl_slist_append(context->m_customHeaders, acceptLanguageString.latin1().data());

    }
}

void ResourceHandleManager::setupHttpMethod(ResourceHandleContext* context)
{
    switch (context->m_method) {
    case HttpMethod_Get:
        curl_easy_setopt(context->m_handle, CURLOPT_HTTPGET, TRUE);
        break;
    case HttpMethod_Post:
        Manx::NetworkProfile::mountUploadDir();
        context->m_httpState |= HttpState_IsPost;
        if (context->m_httpState & HttpState_IsSyncReq)
            setupPOST(context->job, &context->m_customHeaders);
        else
            setupPOSTAsync(context, &context->m_customHeaders);
        break;
    case HttpMethod_Put:
        if (context->m_httpState & HttpState_IsSyncReq)
            setupPUT(context, &context->m_customHeaders);
        else
            setupPUTAsync(context, &context->m_customHeaders);
        break;
    case HttpMethod_Head:
        curl_easy_setopt(context->m_handle, CURLOPT_NOBODY, TRUE);
        break;
    case HttpMethod_Delete:
        curl_easy_setopt(context->m_handle, CURLOPT_CUSTOMREQUEST, "DELETE");
        break;
    case HttpMethod_Options:
        curl_easy_setopt(context->m_handle, CURLOPT_CUSTOMREQUEST, "OPTIONS");
        break;
    }

    if (context->m_customHeaders)
        curl_easy_setopt(context->m_handle, CURLOPT_HTTPHEADER, context->m_customHeaders);
}

void ResourceHandleManager::initializeHandleForSync(ResourceHandleContext* context)
{
    ASSERT(context && context->job);

    context->m_httpState |= HttpState_IsSyncReq;
    KURL kurl = context->job->firstRequest().url();

    // Remove any fragment part, otherwise curl will send it as part of the request.
    kurl.removeFragmentIdentifier();

    ResourceHandleInternal* d = context->job->getInternal();
    String url = kurl.string();

#ifdef ENABLE_PERSISTENT_CONNECTION_FOR_SYNCHRONOUS_COMMUNICATION
    d->m_handle = curlEasyHandleForSync();
#else
    d->m_handle = curl_easy_init();
#endif
    context->m_handle = d->m_handle;

    const HTTPHeaderMap headerFields =  context->job->firstRequest().httpHeaderFields(); 
    setCustomHeaders(headerFields, context);
    initializeCurlHandle(d->m_handle, context, headerCallback, writeCallback, seekCallback);

    if (Manx::NetworkProfile::get(Manx::NetPropsSslVerifyPeer)) {
        curl_easy_setopt(d->m_handle, CURLOPT_SSL_CTX_FUNCTION, sslctxfun);
        curl_easy_setopt(d->m_handle, CURLOPT_SSL_CTX_DATA, context);
    }

    // url must remain valid through the request
    ASSERT(!d->m_url);

    // url is in ASCII so latin1() will only convert it to char* without character translation.
    d->m_url = fastStrDup(url.latin1().data());
    curl_easy_setopt(d->m_handle, CURLOPT_URL, d->m_url);

    DEBUG_LOG("-> initializeCurlHandle(sync) url [%s]\n", context->m_rawUrl);
    setupHttpMethod(context);
}

void ResourceHandleManager::initializeHandleForAsync(ResourceHandleContext* context)
{
#if OS(ORBIS)
    ASSERT(isWorkerThread());
#endif
    ASSERT(context);
    KURL kurl = context->m_KURL;

    // Remove any fragment part, otherwise curl will send it as part of the request.
    kurl.removeFragmentIdentifier();

    String url = kurl.string();

    if (kurl.isLocalFile()) {
        String query = kurl.query();
        // Remove any query part sent to a local file.
        if (!query.isEmpty()) {
            int queryIndex = url.find(query);
            if (queryIndex != -1)
                url = url.left(queryIndex - 1);
        }

        // Determine the MIME type based on the path.
        ResourceHandleCommand* cmd = new ResourceHandleCommand();
        ASSERT(cmd);
        cmd->m_data[0].typeVoidPtr = context->job;

        size_t datalen = strlen(url.ascii().data());
        cmd->m_data[1].typeCharPtr = new char[datalen + 1];
        ASSERT(cmd->m_data[1].typeCharPtr);
        strncpy(cmd->m_data[1].typeCharPtr, url.ascii().data(), datalen);
        cmd->m_data[1].typeCharPtr[datalen] = '\0';
        callOnMainThread(handleSetMimeType, cmd);
    }

    context->m_handle = curl_easy_init();

    if (kurl.host() == "psvitareg.trendmicro.com") {
        curl_easy_setopt(context->m_handle, CURLOPT_SSL_SESSIONID_CACHE, 0L);
        curl_easy_setopt(context->m_handle, CURLOPT_FRESH_CONNECT, 1L);
        curl_easy_setopt(context->m_handle, CURLOPT_FORBID_REUSE, 1L);
    }

    if (context->m_httpState & HttpState_DefersLoading) {
        CURLcode error = curl_easy_pause(context->m_handle, CURLPAUSE_ALL);
        // If we did not pause the handle, we would ASSERT in the
        // header callback(async). So just assert here.
        ASSERT_UNUSED(error, error == CURLE_OK);
    }

    initializeCurlHandle(context->m_handle, context, headerCallbackAsync, writeCallbackAsync, seekCallbackAsync);

    if (Manx::NetworkProfile::get(Manx::NetPropsSslVerifyPeer)) {
        curl_easy_setopt(context->m_handle, CURLOPT_SSL_CTX_FUNCTION, sslctxfun);
        curl_easy_setopt(context->m_handle, CURLOPT_SSL_CTX_DATA, context);
    }

    if (context->m_rawUrl) {
        fastFree(context->m_rawUrl); 
        context->m_rawUrl = 0;
    }

    // url is in ASCII so latin1() will only convert it to char* without character translation.
    context->m_rawUrl = fastStrDup(url.latin1().data());
    curl_easy_setopt(context->m_handle, CURLOPT_URL, context->m_rawUrl);

    DEBUG_LOG("-> initializeCurlHandle url [%s]\n", context->m_rawUrl);
    setupHttpMethod(context);

    // set auth credential if it exists.
    if (!context->m_httpUserPass.isEmpty()) {
        curl_easy_setopt(context->m_handle, CURLOPT_HTTPAUTH, context->m_httpAuthType);
        curl_easy_setopt(context->m_handle, CURLOPT_USERPWD, context->m_httpUserPass.latin1().data());
    }
    if (!context->m_proxyUserPass.isEmpty()) {
        curl_easy_setopt(context->m_handle, CURLOPT_PROXYAUTH, context->m_proxyAuthType);
        curl_easy_setopt(context->m_handle, CURLOPT_PROXYUSERPWD, context->m_proxyUserPass.latin1().data());
        context->m_httpState |= HttpState_ProxyUserPassWasSet;
    }
}

void ResourceHandleManager::initializeHandleForFiltering(ResourceHandleContext* context)
{
    ASSERT(context);

    context->m_filteringhandle = curl_easy_init();
    initializeCurlHandle(context->m_filteringhandle, context, headerCallbackFiltering, writeCallbackFiltering, 0);

    String filteringUserAgentString("User-Agent: ");
    filteringUserAgentString.append(Manx::WebSecurity::userAgent());
    context->m_filteringHeadersSlist = curl_slist_append(context->m_filteringHeadersSlist, filteringUserAgentString.latin1().data());

    if (Manx::NetworkProfile::get(Manx::NetPropsSslVerifyPeer)) {
        curl_easy_setopt(context->m_filteringhandle, CURLOPT_SSL_CTX_FUNCTION, sslctxfun);
        curl_easy_setopt(context->m_filteringhandle, CURLOPT_SSL_CTX_DATA, context);
    }

    curl_easy_setopt(context->m_filteringhandle, CURLOPT_URL, context->m_filteringurl.latin1().data());

    // Should we support other methods than GET?
    curl_easy_setopt(context->m_filteringhandle, CURLOPT_HTTPGET, TRUE);

    if (context->m_filteringHeadersSlist)
        curl_easy_setopt(context->m_filteringhandle, CURLOPT_HTTPHEADER, context->m_filteringHeadersSlist);
}

void ResourceHandleManager::setCookies(const KURL& url, const String& value)
{
    if (!Manx::NetworkProfile::cookieEnabled())
        return;

    String host = url.host();
    String path = url.path();

    DEBUG_LOG("setCookies(): url.host=%s\n", host.utf8().data());
    DEBUG_LOG("setCookies(): url.path=%s\n", path.utf8().data());
    DEBUG_LOG("setCookies(): value=%s\n", value.utf8().data());

    Manx::Cookie *cookie = Manx::Cookie::create(value.utf8().data(), host.utf8().data(), path.utf8().data());
    if (!cookie) {
        DEBUG_LOG("setCookies(): BAD COOKIE\n");
        return;
    }

#if 0
    DEBUG_LOG("NAME    : %s\n", cookie->name());
    DEBUG_LOG("VALUE   : %s\n", cookie->value());
    DEBUG_LOG("DOMAIN  : %s\n", cookie->domain());
    DEBUG_LOG("PATH    : %s\n", cookie->path());
    DEBUG_LOG("EXPIRE  : %s\n", cookie->expire());
    DEBUG_LOG("MAXAGE  : %s\n", cookie->maxAge());
    DEBUG_LOG("SECURE  : %s\n", cookie->secure() ? "true" : "false");
    DEBUG_LOG("HTTPONLY: %s\n", cookie->httpOnly() ? "true" : "false");
#endif

    if (cookie->httpOnly()) {
        DEBUG_LOG("setCookies(): HTTP ONLY COOKIE\n");
        delete cookie;
        return;
    }

    const char* netscapeCookie = cookie->netscapeCookie();
    if (netscapeCookie) {
        // set the cookie in curl
        CURL* handle = curl_easy_init();
#if DEBUG_CURL
            curl_easy_setopt(handle, CURLOPT_VERBOSE, 1);
#endif
        curl_easy_setopt(handle, CURLOPT_SHARE, m_curlShareHandle);
        curl_easy_setopt(handle, CURLOPT_URL, url.string().latin1().data());
        curl_easy_setopt(handle, CURLOPT_COOKIELIST, netscapeCookie);
        curl_easy_cleanup(handle);
        DEBUG_LOG("<ResourceHandleManager::setCookies(): %s>\n", netscapeCookie);
    }

    delete cookie;
}

void ResourceHandleManager::saveCookies()
{
    char cookieJarFileNameBuffer[256] = { }; 
    char* cookieJarFileName = m_cookieJarFileName;
    if (!cookieJarFileName) {
        Manx::NetworkProfile::get(Manx::NetPropsCookieFilename, cookieJarFileNameBuffer, sizeof(cookieJarFileNameBuffer));
        cookieJarFileName = cookieJarFileNameBuffer;
    }

#if USE_SCRACHPAD_COOKIEJAR
    char* scrachpadFileName = 0;
    if (scrachpadmatch(cookieJarFileName)) {
        scrachpadFileName = makescrachpadpath(cookieJarFileName);
        cookieJarFileName = scrachpadFileName;
    }
#endif

    long long size = 0;
    if (getFileSize(cookieJarFileName, size)) {
        if (size > MAX_COOKIE_JAR_FILE_SIZE) {
            DEBUG_LOG("<ResourceHandleManager::saveCookies() file size over. :%s: size:%lld>\n", cookieJarFileName, size);
            if (!deleteAllCookies())
                abort();
            return;
        }
    }
    DEBUG_LOG("<ResourceHandleManager::saveCookies file:%s :size:%lld>\n", cookieJarFileName, size);

    // save cookie list to file 
    CURL* handle = curl_easy_init();

#if DEBUG_CURL
        curl_easy_setopt(handle, CURLOPT_VERBOSE, 1);
#endif

    cookieJarFileName = m_cookieJarFileName;
    if (!cookieJarFileName) {
        Manx::NetworkProfile::get(Manx::NetPropsCookieFilename, cookieJarFileNameBuffer, sizeof(cookieJarFileNameBuffer));
        cookieJarFileName = cookieJarFileNameBuffer;
    }

    if (Manx::NetworkProfile::cookieEnabled()) {
        curl_easy_setopt(handle, CURLOPT_SHARE, m_curlShareHandle);
        curl_easy_setopt(handle, CURLOPT_COOKIEJAR, cookieJarFileName);
        curl_easy_setopt(handle, CURLOPT_COOKIELIST, "FLUSH");
        DEBUG_LOG("<ResourceHandleManager::saveCookies file:%s>\n", cookieJarFileName);
    } else {
        curl_easy_setopt(handle, CURLOPT_SHARE, m_curlShareHandle);
        curl_easy_setopt(handle, CURLOPT_COOKIELIST, "ALL");
    }
    curl_easy_cleanup(handle);

#if USE_SCRACHPAD_COOKIEJAR
    freescrachpadpath(scrachpadFileName);
#endif

}

void ResourceHandleManager::initializeCurlHandle(CURL* handle, ResourceHandleContext* context, curl_write_callback headerCallback, curl_write_callback writecallback, curl_seek_callback seekCallback)
{
    Manx::ResourceRequestLogger::startIfEnable(handle);

    long timeoutMilliSeconds = 0;
    ResourceHandleInternal* d = context->job->getInternal();
    double timeoutSeconds = d->m_firstRequest.timeoutInterval();
    if (timeoutSeconds > 0)
        timeoutMilliSeconds = timeoutSeconds * 1000;

    // FIXME: The version of CURL library for PSVITA should be same as ORBIS.
#if LIBCURL_VERSION_NUM < 0x072200 // Earlier than libcurl 7.34.0
    curl_easy_setopt(handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
#else
    curl_easy_setopt(handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_0); // FIXME: To avoid SSL handshake issue on OpenSSL, TLSv1.0 is used.
#endif

    curl_easy_setopt(handle, CURLOPT_PRIVATE, context);
    curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, m_curlErrorBuffer);
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writecallback);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, context);
    curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerCallback);
    curl_easy_setopt(handle, CURLOPT_WRITEHEADER, context);
    curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, Manx::NetworkProfile::get(Manx::NetPropsFollowLocation));
    curl_easy_setopt(handle, CURLOPT_MAXREDIRS, Manx::NetworkProfile::get(Manx::NetPropsMaxRedirs));
    curl_easy_setopt(handle, CURLOPT_SHARE, m_curlShareHandle);
    curl_easy_setopt(handle, CURLOPT_DNS_CACHE_TIMEOUT, s_dnsCacheTimeout); // 5 minutes
    curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, timeoutMilliSeconds); // libcurl default transfer timeout is 0 which means it never times out.
    curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, s_connectTimeout); // libcurl default connection timeout is 300 seconds.
    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, Manx::NetworkProfile::get(Manx::NetPropsSslVerifyHost));
    curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, Manx::NetworkProfile::get(Manx::NetPropsSslVerifyPeer));
    curl_easy_setopt(handle, CURLOPT_SSL_CIPHER_LIST, Manx::NetworkProfile::getString(Manx::NetPropsSslCipherList));
    curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    if (seekCallback) {
        curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, seekCallback);
        curl_easy_setopt(handle, CURLOPT_SEEKDATA, reinterpret_cast<void*>(context));
    }

    // If you want to use file type CAINFO, you can activate following codes.
    // CAINFO is loaded from memory every creating curl handle on MANX port.
    // (see also loadRootServerCerts())
    // char caPath[256] = {};
    // Manx::NetworkProfile::get(Manx::NetPropsServerCertPath, caPath, sizeof(caPath));
    // if (strlen(caPath) > 0)
    //    curl_easy_setopt(handle, CURLOPT_CAINFO, caPath);

    // enable gzip and deflate through Accept-Encoding:
    char acceptEncoding[256] = { }; 
    Manx::NetworkProfile::get(Manx::NetPropsEncoding, acceptEncoding, sizeof(acceptEncoding));
    curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, acceptEncoding);

    // Set cookie options 
    if (!Manx::NetworkProfile::cookieEnabled())
        curl_easy_setopt(handle, CURLOPT_COOKIELIST, "ALL");

    // check network 
    if (context->m_isMainResource)
        Manx::NetworkProfile::netCtlCheck();

    // Set proxy options if we have them.
    if (Manx::NetworkProfile::proxyEnabled()
        && (s_filteringThreadId != currentThread())
        && !Manx::WebSecurity::isBlockPageResourceURL(context->m_KURL.string().latin1().data())
        && !Manx::WebSecurity::isUnblockedDomain(context->m_KURL.string().latin1().data())) {
        
        char proxyServer[256];
        Manx::NetworkProfile::get(Manx::NetPropsProxy, proxyServer, sizeof(proxyServer));
        curl_easy_setopt(handle, CURLOPT_PROXY, proxyServer);
        curl_easy_setopt(handle, CURLOPT_PROXYTYPE, Manx::NetworkProfile::get(Manx::NetPropsProxyType));

        char proxyUserPass[256];
        Manx::NetworkProfile::get(Manx::NetPropsUserPass, proxyUserPass, sizeof(proxyUserPass));
        context->m_preSetProxyUserPass = "";
        context->m_preSetProxyUserPass.append((const LChar*)proxyUserPass);

        // set proxy auth credential if it exsits.
        ProtectionSpaceServerType type;
        ProtectionSpaceAuthenticationScheme scheme;
        KURL proxyUrl(KURL(), (const LChar*)proxyServer);
        context->m_proxyKURL = proxyUrl.copy();
        WebCore::Credential savedProxyCredential = CredentialBackingStore::instance()->getLogin(proxyUrl, type, scheme);
        if (!savedProxyCredential.isEmpty()) {
            context->m_proxyUserPass = "";
            context->m_proxyUserPass.append(savedProxyCredential.user());
            context->m_proxyUserPass.append(":");
            context->m_proxyUserPass.append(savedProxyCredential.password());
            context->m_proxyAuthType = (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic)?CURLAUTH_BASIC:CURLAUTH_DIGEST;

            curl_easy_setopt(handle, CURLOPT_PROXYAUTH, context->m_proxyAuthType);
            curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, context->m_proxyUserPass.latin1().data());
            context->m_httpState |= HttpState_HasProxyCredential;
        }

    }

    // set auth credential if it exists.
    ProtectionSpaceServerType type;
    ProtectionSpaceAuthenticationScheme scheme;
    WebCore::Credential savedHttpCredential = CredentialBackingStore::instance()->getLogin(context->m_KURL, type, scheme);
    if (!savedHttpCredential.isEmpty()) {
        context->m_httpUserPass = "";
        context->m_httpUserPass.append(savedHttpCredential.user());
        context->m_httpUserPass.append(":");
        context->m_httpUserPass.append(savedHttpCredential.password());
        context->m_httpAuthType = (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic)?CURLAUTH_BASIC:CURLAUTH_DIGEST;

        curl_easy_setopt(handle, CURLOPT_HTTPAUTH, context->m_httpAuthType);
        curl_easy_setopt(handle, CURLOPT_USERPWD, context->m_httpUserPass.latin1().data());
        context->m_httpState |= (HttpState_HttpAuthInProcess | HttpState_HasHttpCredential);
    }

    // allow bad ssl domain if it is already allowed.
    if ((context->m_httpState & HttpState_IsHttps)
        && s_userAcceptedHttpsDomains.contains(StringHash::hash(context->m_KURL.host()))) {
        curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, false);
        curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, false);
    }

}

String ResourceHandleManager::cookies(const KURL& url)
{
    String cookie;
    struct curl_slist *list;

    if (!Manx::NetworkProfile::cookieEnabled()) 
        return cookie;

    // get the cookie list in curl
    CURL* handle = curl_easy_init();
#if DEBUG_CURL
        curl_easy_setopt(handle, CURLOPT_VERBOSE, 1);
#endif
    curl_easy_setopt(handle, CURLOPT_SHARE, m_curlShareHandle);
    curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &list);
    curl_easy_cleanup(handle);

    // curl cookie format (netscape/mozilla cookie file format)
    // 7 tab separated fields in the following specific order:
    //
    // 0: domain    - domain that set & can subsequently read the cookie
    // 1: tailmatch - TRUE or FALSE, if machines under that domain can read the cookie
    // 2: path      - root path under domain where the cookie is valid. If '/', cookie is valid for entire domain.
    // 3: secure    - TRUE or FALSE, if a secure connection (HTTPS) is required to read the cookie.
    // 4: expires   - Unix Time in seconds when the cookie is set to expire.
    // 5: name      - name of the value that the cookie is storing/saving.
    // 6: value     - value of the cookie

    double now = currentTime();
    // iterate through the cookie list to look for a match
    for (struct curl_slist *cur = list; cur; cur = cur->next) {
        String data(cur->data);
        Vector<String> bits;
        data.split("\t", bits);

        // http only cookies shouldn't go to javascript
        if (bits[0].startsWith("#HttpOnly_"))
            continue;

        // domain
        if (!bits[0].isEmpty()) {
            // tailmatch
            if (equalIgnoringCase(bits[1], "TRUE")) {
                // if (!url.host().endsWith(bits[0])) {
                // curl prepends '.' to the domain so make sure handle case where domain is http://domain.com
                if (!url.host().endsWith(bits[0]) && String(bits[0]) != String(String(".") + url.host()))
                    continue;
            } else {
                if (url.host() != bits[0])
                    continue;
            }
        }

        // path
        if (!bits[2].isEmpty()) {
            if (!url.path().startsWith(bits[2]))
                continue;
        }

        // secure
        if (equalIgnoringCase(bits[3], "TRUE")) {
            if (!(url.protocolIs("https")))
                continue;
        }

        // expires
        double exp = bits[4].toDouble();
        if (exp && exp < now)
            continue;

        // append to existing string seperated with semi-colon
        if (!cookie.isEmpty())
            cookie.append("; ");

        // sometimes name=value pair has empty value so handle accordingly
        cookie.append(bits[5] + "=" + (bits.size() == 7 ? bits[6] : ""));
    }

    // free up allocated resources
    curl_slist_free_all(list);

    DEBUG_LOG("<ResourceHandleManager::cookies getcookie= %s> \n", cookie.latin1().data());
    return cookie;
}

int ResourceHandleManager::deleteAllCookies()
{
    int ret = 0;
    CURL* handle = curl_easy_init();
    curl_easy_setopt(handle, CURLOPT_SHARE, m_curlShareHandle);

    char cookieJarFileNameBuffer[256] = { };
    char* cookieJarFileName = m_cookieJarFileName;
    if (!cookieJarFileName) {
        Manx::NetworkProfile::get(Manx::NetPropsCookieFilename, cookieJarFileNameBuffer, sizeof(cookieJarFileNameBuffer));
        cookieJarFileName = cookieJarFileNameBuffer;
    }

#if USE_SCRACHPAD_COOKIEJAR
    char* scrachpadFileName = 0;
    if (scrachpadmatch(cookieJarFileName)) {
        scrachpadFileName = makescrachpadpath(cookieJarFileName);
        cookieJarFileName = scrachpadFileName;
        clearscrachpad();
    }
#endif

    DEBUG_LOG("<ResourceHandleManager::deleteAllCookies() FileName=%s> \n", cookieJarFileName);
    if (cookieJarFileName)
        ret = remove(cookieJarFileName);
    curl_easy_setopt(handle, CURLOPT_COOKIELIST, "ALL");
    curl_easy_cleanup(handle);

#if USE_SCRACHPAD_COOKIEJAR
    if (scrachpadFileName)
        freescrachpadpath(scrachpadFileName);
#endif
    return ret;
}

void ResourceHandleManager::loadCookies()
{
    if (!Manx::NetworkProfile::cookieEnabled()) 
        return;

    CURL* handle = curl_easy_init();
#if DEBUG_CURL
    curl_easy_setopt(handle, CURLOPT_VERBOSE, 1);
#endif
    curl_easy_setopt(handle, CURLOPT_SHARE, m_curlShareHandle);

    char cookieJarFileNameBuffer[256] = { };
    char* cookieJarFileName = m_cookieJarFileName;
    if (!cookieJarFileName) {
        Manx::NetworkProfile::get(Manx::NetPropsCookieFilename, cookieJarFileNameBuffer, sizeof(cookieJarFileNameBuffer));
        cookieJarFileName = cookieJarFileNameBuffer;
    }

    curl_easy_setopt(handle, CURLOPT_COOKIEFILE, cookieJarFileName);
    curl_easy_setopt(handle, CURLOPT_COOKIELIST, "LOAD");
    curl_easy_cleanup(handle);
}

void ResourceHandleManager::flushCookies()
{
    if (!Manx::NetworkProfile::cookieEnabled()) 
        return;

    CURL* handle = curl_easy_init();
#if DEBUG_CURL
    curl_easy_setopt(handle, CURLOPT_VERBOSE, 1);
#endif

    char cookieJarFileNameBuffer[256] = { };
    char* cookieJarFileName = m_cookieJarFileName;
    if (!cookieJarFileName) {
        Manx::NetworkProfile::get(Manx::NetPropsCookieFilename, cookieJarFileNameBuffer, sizeof(cookieJarFileNameBuffer));
        cookieJarFileName = cookieJarFileNameBuffer;
    }

    curl_easy_setopt(handle, CURLOPT_COOKIEJAR, cookieJarFileName);
    curl_easy_setopt(handle, CURLOPT_COOKIELIST, "FLUSH");
    curl_easy_cleanup(handle);

}

void ResourceHandleManager::cleanupSessionCookies()
{
    if (!Manx::NetworkProfile::cookieEnabled()) 
        return;

    CURL* handle = curl_easy_init();
#if DEBUG_CURL
    curl_easy_setopt(handle, CURLOPT_VERBOSE, 1);
#endif
    curl_easy_setopt(handle, CURLOPT_SHARE, m_curlShareHandle);

    char cookieJarFileNameBuffer[256] = { };
    Manx::NetworkProfile::get(Manx::NetPropsCookieFilename, cookieJarFileNameBuffer, sizeof(cookieJarFileNameBuffer));
    strncpy(m_cookieJarFileName, cookieJarFileNameBuffer, sizeof(m_cookieJarFileName));

    curl_easy_setopt(handle, CURLOPT_COOKIEJAR, m_cookieJarFileName);

    if (getenv("WEBKIT_NET_SKIP_CLEAR_SESSCOOKIE")) {
        DEBUG_LOG("<cleanup SessionCookies from CookieJarFile>\n");
        curl_easy_setopt(handle, CURLOPT_COOKIELIST, "SESS");
        curl_easy_setopt(handle, CURLOPT_COOKIELIST, "FLUSH");
    }

    curl_easy_cleanup(handle);
}

void ResourceHandleManager::cancel(ResourceHandle* job)
{
    DEBUG_LOG("ResourceHandleManager::cancel url = [%s]\n", job->firstRequest().url().string().latin1().data());

    if (removeScheduledJob(job))
        return;

#if OS(ORBIS)
    {   WTF::MutexLocker lock(*s_workerThreadMultiMutex);
        ResourceHandleContext* context = s_jobToContextMap.get(job);
        if (context) {
            s_cancelContextList.append(context);
            DEBUG_LOG("-> RHM::cancel wakeupSelect()\n");
            wakeupSelect(s_WPIPE[REQUEST_CANCEL]);
        }
    }
#endif

#if OS(PSP2)
    {
        WTF::MutexLocker lock(*s_workerThreadMultiMutex);
        ResourceHandleContext* context = s_jobToContextMap.get(job);
        if (context)
            context->m_httpState |= HttpState_Cancelled;
    }
#endif

	ASSERT(job && job->getInternal());
    job->getInternal()->m_cancelled = true;
}

static CURLcode sslctxfun(CURL* curlhandle, void* sslctx, void* param)
{
    Manx::Ssl::sslctxfun(sslctx, param);
    return CURLE_OK;
}

static void replaceUrlOfMainResource(void* data)
{
    WebCore::ResourceHandleContext* context = reinterpret_cast<WebCore::ResourceHandleContext*>(data);
    ASSERT(context && context->m_nwcontext);
    if ((WebCore::s_filteringThreadCreated && Manx::WebSecurity::activatedStatus())
        || (Manx::NetworkProfile::proxyEnabled() && Manx::WebSecurity::isBlockPageResourceURL(context->m_rawUrl))) {
        DEBUG_LOG("-> replaceUrlOfMainResource(context->m_KURL)\n");
        context->m_nwcontext->replaceUrlOfMainResource(context->m_KURL);
    }
}

} // namespace WebCore

#endif // !USE(NTF)
